<?php

namespace MintyPHP\Module\Audit\Repository;

use MintyPHP\DB;
use MintyPHP\Module\Audit\Domain\ImportAuditStatus;
use MintyPHP\Repository\Support\RepoQuery;
use MintyPHP\Repository\Support\RepositoryArrayHelper;

/** Tracks CSV import runs including source file, row counts, status, and completion metadata. */
class ImportAuditRunRepository
{
    private const FILTER_OPTIONS_LIMIT_MAX = 200;

    public function createRunning(array $data): int|false
    {
        $id = DB::insert(
            'insert into import_audit_runs (
                run_uuid, profile_key, status, source_filename, mapped_targets_csv, started_at, user_id, current_tenant_id
            ) values (?,?,?,?,?,NOW(),?,?)',
            (string) ($data['run_uuid'] ?? ''),
            (string) ($data['profile_key'] ?? ''),
            (string) ($data['status'] ?? ImportAuditStatus::Running->value),
            $data['source_filename'] ?? null,
            $data['mapped_targets_csv'] ?? null,
            $data['user_id'] !== null ? (string) ((int) $data['user_id']) : null,
            $data['current_tenant_id'] !== null ? (string) ((int) $data['current_tenant_id']) : null
        );

        return $id ? (int) $id : false;
    }

    public function finishById(int $id, array $data): bool
    {
        if ($id <= 0) {
            return false;
        }

        $updated = DB::update(
            'update import_audit_runs
             set status = ?,
                 rows_total = ?,
                 created_count = ?,
                 skipped_count = ?,
                 failed_count = ?,
                 error_codes_json = ?,
                 duration_ms = ?,
                 finished_at = NOW()
             where id = ?',
            (string) ($data['status'] ?? ImportAuditStatus::Failed->value),
            (string) ((int) ($data['rows_total'] ?? 0)),
            (string) ((int) ($data['created_count'] ?? 0)),
            (string) ((int) ($data['skipped_count'] ?? 0)),
            (string) ((int) ($data['failed_count'] ?? 0)),
            $data['error_codes_json'] ?? null,
            $data['duration_ms'] !== null ? (string) ((int) $data['duration_ms']) : null,
            (string) $id
        );

        return (int) $updated > 0;
    }

    public function listPaged(array $filters): array
    {
        $search = trim((string) ($filters['search'] ?? ''));
        $profileKey = strtolower(trim((string) ($filters['profile_key'] ?? '')));
        $status = strtolower(trim((string) ($filters['status'] ?? '')));
        $createdFrom = trim((string) ($filters['created_from'] ?? ''));
        $createdTo = trim((string) ($filters['created_to'] ?? ''));
        $userIds = array_slice(
            RepoQuery::normalizeIdList($filters['user_ids'] ?? ''),
            0,
            self::FILTER_OPTIONS_LIMIT_MAX
        );

        [$limit, $offset] = RepoQuery::sanitizeLimitOffset($filters, 20, 1, 200, 0);
        [$order, $dir] = RepoQuery::sanitizeOrder(
            $filters,
            [
                'id',
                'started_at',
                'finished_at',
                'duration_ms',
                'rows_total',
                'created_count',
                'skipped_count',
                'failed_count',
                'status',
                'profile_key',
            ],
            'started_at',
            'desc'
        );

        $where = [];
        $params = [];

        RepoQuery::addLikeFilter(
            $where,
            $params,
            [
                'import_audit_runs.run_uuid',
                'import_audit_runs.source_filename',
                'import_audit_runs.error_codes_json',
            ],
            $search
        );

        if (in_array($profileKey, ['users', 'departments'], true)) {
            $where[] = 'import_audit_runs.profile_key = ?';
            $params[] = $profileKey;
        }
        if (ImportAuditStatus::tryNormalize($status) !== null) {
            $where[] = 'import_audit_runs.status = ?';
            $params[] = $status;
        }
        if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $createdFrom)) {
            $where[] = 'import_audit_runs.started_at >= ?';
            $params[] = $createdFrom . ' 00:00:00';
        }
        if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $createdTo)) {
            $where[] = 'import_audit_runs.started_at <= ?';
            $params[] = $createdTo . ' 23:59:59';
        }
        if ($userIds !== []) {
            $where[] = 'import_audit_runs.user_id in (???)';
            $params[] = $userIds;
        }

        $whereSql = $where ? (' where ' . implode(' and ', $where)) : '';
        $fromSql = ' from import_audit_runs ' .
            'left join users on users.id = import_audit_runs.user_id ' .
            'left join tenants on tenants.id = import_audit_runs.current_tenant_id ';

        $total = (int) (DB::selectValue('select count(*)' . $fromSql . $whereSql, ...$params) ?? 0);
        $rows = DB::select(
            'select
                import_audit_runs.id,
                import_audit_runs.run_uuid,
                import_audit_runs.profile_key,
                import_audit_runs.status,
                import_audit_runs.source_filename,
                import_audit_runs.mapped_targets_csv,
                import_audit_runs.rows_total,
                import_audit_runs.created_count,
                import_audit_runs.skipped_count,
                import_audit_runs.failed_count,
                import_audit_runs.error_codes_json,
                import_audit_runs.started_at,
                import_audit_runs.finished_at,
                import_audit_runs.duration_ms,
                import_audit_runs.user_id,
                import_audit_runs.current_tenant_id,
                users.id,
                users.uuid,
                users.display_name,
                users.email,
                tenants.id,
                tenants.uuid,
                tenants.description
            ' . $fromSql . $whereSql .
            sprintf(' order by import_audit_runs.`%s` %s limit ? offset ?', $order, $dir),
            ...array_merge($params, [(string) $limit, (string) $offset])
        );

        $normalized = [];
        if (is_array($rows)) {
            foreach ($rows as $row) {
                $item = $this->normalizeRow($row);
                if ($item !== null) {
                    $normalized[] = $item;
                }
            }
        }

        return [
            'total' => $total,
            'rows' => $normalized,
        ];
    }

    public function listFilterOptions(int $limit = self::FILTER_OPTIONS_LIMIT_MAX): array
    {
        $limit = max(1, min(self::FILTER_OPTIONS_LIMIT_MAX, $limit));

        $userRows = DB::select(
            'select
                import_audit_runs.user_id,
                max(import_audit_runs.started_at) as last_used_at,
                max(users.id) as user_exists_id,
                max(users.display_name) as user_display_name,
                max(users.email) as user_email
            from import_audit_runs
            left join users on users.id = import_audit_runs.user_id
            where import_audit_runs.user_id is not null
              and import_audit_runs.user_id > 0
            group by import_audit_runs.user_id
            order by last_used_at desc
            limit ?',
            (string) $limit
        );

        $users = [];
        if (is_array($userRows)) {
            foreach ($userRows as $row) {
                $flat = RepositoryArrayHelper::flattenRow($row);
                $id = (int) ($flat['user_id'] ?? 0);
                if ($id <= 0) {
                    continue;
                }
                $users[] = [
                    'id' => $id,
                    'display_name' => trim((string) ($flat['user_display_name'] ?? '')),
                    'email' => trim((string) ($flat['user_email'] ?? '')),
                    'exists' => (int) ($flat['user_exists_id'] ?? 0) > 0,
                ];
            }
        }

        return ['users' => $users];
    }

    public function find(int $id): ?array
    {
        if ($id <= 0) {
            return null;
        }

        $row = DB::selectOne(
            'select
                import_audit_runs.id,
                import_audit_runs.run_uuid,
                import_audit_runs.profile_key,
                import_audit_runs.status,
                import_audit_runs.source_filename,
                import_audit_runs.mapped_targets_csv,
                import_audit_runs.rows_total,
                import_audit_runs.created_count,
                import_audit_runs.skipped_count,
                import_audit_runs.failed_count,
                import_audit_runs.error_codes_json,
                import_audit_runs.started_at,
                import_audit_runs.finished_at,
                import_audit_runs.duration_ms,
                import_audit_runs.user_id,
                import_audit_runs.current_tenant_id,
                users.id,
                users.uuid,
                users.display_name,
                users.email,
                tenants.id,
                tenants.uuid,
                tenants.description
            from import_audit_runs
            left join users on users.id = import_audit_runs.user_id
            left join tenants on tenants.id = import_audit_runs.current_tenant_id
            where import_audit_runs.id = ?
            limit 1',
            (string) $id
        );

        return $this->normalizeRow($row);
    }

    public function purgeOlderThanDays(int $days): int
    {
        if ($days <= 0) {
            return 0;
        }

        $cutoff = (new \DateTimeImmutable('now', new \DateTimeZone('UTC')))
            ->modify('-' . $days . ' days')
            ->format('Y-m-d H:i:s');

        $deleted = DB::delete('delete from import_audit_runs where started_at < ?', $cutoff);
        return is_int($deleted) ? $deleted : 0;
    }

    private function normalizeRow(mixed $row): ?array
    {
        if (!is_array($row)) {
            return null;
        }

        $item = $row['import_audit_runs'] ?? [];
        if (!is_array($item) || !isset($item['id'])) {
            return null;
        }

        $user = is_array($row['users'] ?? null) ? $row['users'] : [];
        $tenant = is_array($row['tenants'] ?? null) ? $row['tenants'] : [];

        $item['user_uuid'] = (string) ($user['uuid'] ?? '');
        $item['user_display_name'] = (string) ($user['display_name'] ?? '');
        $item['user_email'] = (string) ($user['email'] ?? '');
        $item['current_tenant_uuid'] = (string) ($tenant['uuid'] ?? '');
        $item['current_tenant_description'] = (string) ($tenant['description'] ?? '');

        return $item;
    }

}
