<?php

namespace MintyPHP\Module\Security\Repository;

use MintyPHP\DB;
use MintyPHP\Repository\Support\RepoQuery;

/**
 * SQL access for security_checks. Tenant-scoped, prepared statements only.
 */
class SecurityCheckRepository
{
    /**
     * @param array<string, mixed> $data
     * @return int|null Inserted ID or null on failure
     */
    public function insert(int $tenantId, array $data): ?int
    {
        $result = DB::insert(
            'INSERT INTO security_checks'
            . ' (tenant_id, debitor_no, debitor_name, domain_no, domain_url, product_code, product_name,'
            . ' template_id, owner_user_id, status, process_state_json, tech_schema_snapshot_json, tech_state_json, created_by)'
            . ' VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
            (string) $tenantId,
            (string) ($data['debitor_no'] ?? ''),
            (string) ($data['debitor_name'] ?? ''),
            (string) ($data['domain_no'] ?? ''),
            (string) ($data['domain_url'] ?? ''),
            (string) ($data['product_code'] ?? ''),
            (string) ($data['product_name'] ?? ''),
            isset($data['template_id']) ? (string) ((int) $data['template_id']) : null,
            isset($data['owner_user_id']) ? (string) ((int) $data['owner_user_id']) : null,
            (string) ($data['status'] ?? 'open'),
            $data['process_state_json'] ?? null,
            $data['tech_schema_snapshot_json'] ?? null,
            $data['tech_state_json'] ?? null,
            (string) ((int) ($data['created_by'] ?? 0))
        );

        return $result === false ? null : (int) $result;
    }

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

        $row = DB::selectOne(
            'SELECT c.*, uo.display_name AS owner_name, uc.display_name AS created_by_name, uu.display_name AS updated_by_name'
            . ' FROM security_checks c'
            . ' LEFT JOIN users uo ON uo.id = c.owner_user_id'
            . ' LEFT JOIN users uc ON uc.id = c.created_by'
            . ' LEFT JOIN users uu ON uu.id = c.updated_by'
            . ' WHERE c.id = ? AND c.tenant_id = ? LIMIT 1',
            (string) $id,
            (string) $tenantId
        );

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

    /**
     * @param array<string, mixed> $filters
     * @return array{total: int, rows: list<array<string, mixed>>}
     */
    public function listPaged(int $tenantId, array $filters): array
    {
        $search = trim((string) ($filters['search'] ?? ''));
        $status = trim((string) ($filters['status'] ?? ''));
        $view = trim((string) ($filters['view'] ?? ''));

        [$limit, $offset] = RepoQuery::sanitizeLimitOffset($filters, 20, 1, 100, 0);
        [$order, $dir] = RepoQuery::sanitizeOrder(
            $filters,
            ['id', 'debitor_name', 'domain_url', 'product_name', 'status', 'created_at', 'updated_at'],
            'created_at',
            'desc'
        );

        $where = ['c.tenant_id = ?'];
        $params = [(string) $tenantId];

        RepoQuery::addLikeFilter($where, $params, ['c.debitor_name', 'c.debitor_no', 'c.domain_url', 'c.product_name'], $search);

        if ($status !== '' && $status !== 'all') {
            $where[] = 'c.status = ?';
            $params[] = $status;
        } elseif ($view === 'active') {
            $where[] = "c.status IN ('open', 'in_progress')";
        } elseif ($view === 'done') {
            $where[] = "c.status IN ('completed', 'archived')";
        }

        $whereSql = ' WHERE ' . implode(' AND ', $where);
        $total = (int) (DB::selectValue('SELECT COUNT(*) FROM security_checks c' . $whereSql, ...$params) ?? 0);

        $rows = DB::select(
            'SELECT c.*, uo.display_name AS owner_name, uc.display_name AS created_by_name'
            . ' FROM security_checks c'
            . ' LEFT JOIN users uo ON uo.id = c.owner_user_id'
            . ' LEFT JOIN users uc ON uc.id = c.created_by'
            . $whereSql
            . sprintf(' ORDER BY c.`%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];
    }

    /**
     * Active users in the tenant, for owner assignment. Ordered by display name.
     *
     * @return list<array{id: int, display_name: string}>
     */
    public function listTenantUsers(int $tenantId): array
    {
        $rows = DB::select(
            'SELECT u.id, u.display_name FROM users u'
            . ' JOIN user_tenants ut ON ut.user_id = u.id'
            . ' WHERE ut.tenant_id = ? AND u.active = 1'
            . ' ORDER BY u.display_name ASC',
            (string) $tenantId
        );

        $users = [];
        if (is_array($rows)) {
            foreach ($rows as $row) {
                $flat = $this->normalizeRow($row);
                if ($flat === null) {
                    continue;
                }
                $id = (int) ($flat['id'] ?? 0);
                if ($id > 0) {
                    $users[] = ['id' => $id, 'display_name' => (string) ($flat['display_name'] ?? '')];
                }
            }
        }

        return $users;
    }

    /**
     * @param array<string, mixed> $data
     */
    public function updateState(int $tenantId, int $id, array $data): bool
    {
        $result = DB::update(
            'UPDATE security_checks SET process_state_json = ?, tech_state_json = ?, status = ?, updated_by = ?'
            . ' WHERE id = ? AND tenant_id = ?',
            $data['process_state_json'] ?? null,
            $data['tech_state_json'] ?? null,
            (string) ($data['status'] ?? 'open'),
            (string) ((int) ($data['updated_by'] ?? 0)),
            (string) $id,
            (string) $tenantId
        );

        return $result !== false;
    }

    public function updateStatus(int $tenantId, int $id, string $status, int $updatedBy): bool
    {
        $result = DB::update(
            'UPDATE security_checks SET status = ?, updated_by = ? WHERE id = ? AND tenant_id = ?',
            $status,
            (string) $updatedBy,
            (string) $id,
            (string) $tenantId
        );

        return $result !== false;
    }

    public function deleteById(int $tenantId, int $id): bool
    {
        if ($id <= 0) {
            return false;
        }

        return (bool) DB::delete(
            'DELETE FROM security_checks WHERE id = ? AND tenant_id = ?',
            (string) $id,
            (string) $tenantId
        );
    }

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

        $item = [];
        foreach ($row as $key => $value) {
            if (is_array($value)) {
                $item = array_merge($item, $value);
            } else {
                $item[$key] = $value;
            }
        }

        return isset($item['id']) ? $item : null;
    }
}
