<?php

namespace MintyPHP\Module\Helpdesk\Repository;

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

class HandoverRepository
{
    /**
     * @param array<string, mixed> $data
     * @return int|null Inserted ID or null on failure
     */
    public function insert(array $data): ?int
    {
        $result = DB::insert(
            'INSERT INTO helpdesk_handovers '
            . '(tenant_id, debitor_no, debitor_name, product_code, domain_no, domain_url, status, schema_snapshot, field_values, created_by) '
            . 'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
            (string) ($data['tenant_id'] ?? 0),
            (string) ($data['debitor_no'] ?? ''),
            (string) ($data['debitor_name'] ?? ''),
            (string) ($data['product_code'] ?? ''),
            (string) ($data['domain_no'] ?? ''),
            (string) ($data['domain_url'] ?? ''),
            (string) ($data['status'] ?? 'draft'),
            $data['schema_snapshot'] ?? null,
            $data['field_values'] ?? null,
            (string) ($data['created_by'] ?? 0)
        );

        if ($result === false) {
            return null;
        }

        return (int) $result;
    }

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

        $row = DB::selectOne(
            'SELECT h.*,'
            . ' uc.display_name AS created_by_label, uc.uuid AS created_by_uuid,'
            . ' uu.display_name AS updated_by_label, uu.uuid AS updated_by_uuid,'
            . ' ua.display_name AS assigned_to_label, ua.uuid AS assigned_to_uuid, ua.email AS assigned_to_email,'
            . ' ub.display_name AS assigned_by_label, ub.uuid AS assigned_by_uuid, ub.email AS assigned_by_email'
            . ' FROM helpdesk_handovers h'
            . ' LEFT JOIN users uc ON uc.id = h.created_by'
            . ' LEFT JOIN users uu ON uu.id = h.updated_by'
            . ' LEFT JOIN users ua ON ua.id = h.assigned_to'
            . ' LEFT JOIN users ub ON ub.id = h.assigned_by'
            . ' WHERE h.id = ? AND h.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'] ?? ''));
        $productCode = trim((string) ($filters['product_code'] ?? ''));
        $assignedTo = (int) ($filters['assigned_to'] ?? 0);
        $view = trim((string) ($filters['view'] ?? ''));

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

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

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

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

        if ($productCode !== '') {
            $where[] = 'h.product_code = ?';
            $params[] = $productCode;
        }

        if ($assignedTo > 0) {
            $where[] = 'h.assigned_to = ?';
            $params[] = (string) $assignedTo;
        }

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

        $rows = DB::select(
            'SELECT h.*, u.display_name AS created_by_name,'
            . ' ua.display_name AS assigned_to_name,'
            . ' sp.name AS product_name, sp.bc_description AS product_bc_description'
            . ' FROM helpdesk_handovers h'
            . ' LEFT JOIN users u ON u.id = h.created_by'
            . ' LEFT JOIN users ua ON ua.id = h.assigned_to'
            . ' LEFT JOIN helpdesk_software_products sp ON sp.code = h.product_code'
            . $whereSql
            . sprintf(' ORDER BY h.`%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 updateFieldValues(int $tenantId, int $id, string $fieldValuesJson, int $updatedBy): bool
    {
        $result = DB::update(
            'UPDATE helpdesk_handovers SET field_values = ?, updated_by = ? WHERE id = ? AND tenant_id = ?',
            $fieldValuesJson,
            (string) $updatedBy,
            (string) $id,
            (string) $tenantId
        );

        return $result !== false;
    }

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

        return $result !== false;
    }

    public function assign(int $tenantId, int $id, int $assignedTo, int $assignedBy, ?string $dueDate, int $updatedBy): bool
    {
        $result = DB::update(
            'UPDATE helpdesk_handovers'
            . ' SET assigned_to = ?, assigned_by = ?, assigned_at = NOW(), due_date = ?, updated_by = ?'
            . ' WHERE id = ? AND tenant_id = ?',
            (string) $assignedTo,
            (string) $assignedBy,
            ($dueDate !== null && $dueDate !== '') ? $dueDate : null,
            (string) $updatedBy,
            (string) $id,
            (string) $tenantId
        );

        return $result !== false;
    }

    /**
     * @param list<int> $ids
     */
    public function deleteByIds(int $tenantId, array $ids): int
    {
        if ($ids === []) {
            return 0;
        }

        $placeholders = implode(',', array_fill(0, count($ids), '?'));
        $params = array_map('strval', $ids);
        $params[] = (string) $tenantId;

        $result = DB::update(
            'DELETE FROM helpdesk_handovers WHERE id IN (' . $placeholders . ') AND tenant_id = ?',
            ...$params
        );

        return is_int($result) ? $result : 0;
    }

    /**
     * Find all handovers linked to a specific domain.
     *
     * @api Called from DomainDetailService
     * @return list<array<string, mixed>>
     */
    public function findByDomainNo(int $tenantId, string $domainNo): array
    {
        if ($domainNo === '') {
            return [];
        }

        $rows = DB::select(
            'SELECT h.*, u.display_name AS created_by_name,'
            . ' sp.name AS product_name, sp.bc_description AS product_bc_description'
            . ' FROM helpdesk_handovers h'
            . ' LEFT JOIN users u ON u.id = h.created_by'
            . ' LEFT JOIN helpdesk_software_products sp ON sp.code = h.product_code'
            . ' WHERE h.tenant_id = ? AND h.domain_no = ?'
            . ' ORDER BY h.created_at DESC',
            (string) $tenantId,
            $domainNo
        );

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

        return $normalized;
    }

    public function beginTransaction(): void
    {
        DB::handle()->begin_transaction();
    }

    public function commitTransaction(): void
    {
        DB::handle()->commit();
    }

    public function rollbackTransaction(): void
    {
        DB::handle()->rollback();
    }

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

        // JOIN queries return nested arrays keyed by table name — merge all sub-arrays
        $item = [];
        foreach ($row as $key => $value) {
            if (is_array($value)) {
                $item = array_merge($item, $value);
            } else {
                $item[$key] = $value;
            }
        }

        if (!isset($item['id'])) {
            return null;
        }

        return $item;
    }
}
