<?php

namespace MintyPHP\Module\Helpdesk\Service;

use MintyPHP\Module\Helpdesk\Repository\UpdateRepository;

/** @api Called from pages/helpdesk/updates action files */
class UpdateService
{
    public const STATUS_OPEN = 'open';
    public const STATUS_ASSIGNED = 'assigned';

    public const ALL_STATUSES = [
        self::STATUS_OPEN,
        self::STATUS_ASSIGNED,
    ];

    public const CATEGORY_UPDATE = 'UPDATE';
    public const CATEGORY_HOTFIX = 'UPDATE-HF';

    public const UPDATE_CATEGORIES = [
        self::CATEGORY_UPDATE,
        self::CATEGORY_HOTFIX,
    ];

    public function __construct(
        private readonly UpdateRepository $repository,
        private readonly BcODataGateway $bcGateway,
    ) {
    }

    /**
     * Get the merged list of BC update tickets with local assignment data.
     *
     * Fetches all UPDATE/UPDATE-HF tickets from BC, then left-joins with
     * local helpdesk_updates records to enrich with domain/gitea assignments.
     *
     * @param array<string, mixed> $filters  Optional filters: status, category
     * @return array{rows: list<array<string, mixed>>, truncated: bool}
     */
    public function getUpdatesList(int $tenantId, array $filters = []): array
    {
        $bcResult = $this->bcGateway->fetchUpdateTickets();
        $bcTickets = $bcResult['tickets'];
        $truncated = $bcResult['truncated'];

        $localRows = $this->repository->findAllByTenant($tenantId);
        $localByTicketNo = [];
        foreach ($localRows as $local) {
            $ticketNo = trim((string) ($local['ticket_no'] ?? ''));
            if ($ticketNo !== '') {
                $localByTicketNo[$ticketNo] = $local;
            }
        }

        $filterStatus = trim((string) ($filters['status'] ?? ''));
        $filterCategory = trim((string) ($filters['category'] ?? ''));
        $filterTicketState = trim((string) ($filters['ticket_state'] ?? ''));
        $filterSearch = trim((string) ($filters['search'] ?? ''));

        $merged = [];
        foreach ($bcTickets as $ticket) {
            $ticketNo = trim((string) ($ticket['No'] ?? ''));
            if ($ticketNo === '') {
                continue;
            }

            $categoryCode = trim((string) ($ticket['Category_1_Code'] ?? ''));
            if (!in_array($categoryCode, self::UPDATE_CATEGORIES, true)) {
                continue;
            }

            $local = $localByTicketNo[$ticketNo] ?? null;
            $status = $local !== null ? (string) ($local['status'] ?? self::STATUS_OPEN) : self::STATUS_OPEN;
            $ticketState = trim((string) ($ticket['Ticket_State'] ?? ''));

            // Apply filters
            if ($filterStatus !== '' && $filterStatus !== 'all' && $status !== $filterStatus) {
                continue;
            }
            if ($filterCategory !== '' && $filterCategory !== 'all' && $categoryCode !== $filterCategory) {
                continue;
            }
            if ($filterTicketState !== '' && $filterTicketState !== 'all' && $ticketState !== $filterTicketState) {
                continue;
            }

            $debitorNo = trim((string) ($ticket['Customer_No'] ?? ''));
            $debitorName = trim((string) ($ticket['Cust_Name'] ?? ''));

            // Search filter: match against ticket no, debitor name/no, domain url, gitea path
            if ($filterSearch !== '') {
                $searchHaystack = strtolower($ticketNo . ' ' . $debitorName . ' ' . $debitorNo . ' ' . ($local['domain_url'] ?? '') . ' ' . ($local['gitea_path'] ?? ''));
                if (strpos($searchHaystack, strtolower($filterSearch)) === false) {
                    continue;
                }
            }

            $merged[] = [
                'ticket_no' => $ticketNo,
                'ticket_description' => trim((string) ($ticket['Description'] ?? '')),
                'category_code' => $categoryCode,
                'debitor_no' => $debitorNo,
                'debitor_name' => $debitorName,
                'ticket_state' => trim((string) ($ticket['Ticket_State'] ?? '')),
                'support_user' => trim((string) ($ticket['Support_User_Name'] ?? '')),
                'created_on' => trim((string) ($ticket['Created_On'] ?? '')),
                'last_activity' => trim((string) ($ticket['Last_Activity_Date'] ?? '')),
                'domain_no' => (string) ($local['domain_no'] ?? ''),
                'domain_url' => (string) ($local['domain_url'] ?? ''),
                'gitea_path' => (string) ($local['gitea_path'] ?? ''),
                'notes' => (string) ($local['notes'] ?? ''),
                'status' => $status,
                'local_id' => $local !== null ? (int) ($local['id'] ?? 0) : null,
            ];
        }

        return ['rows' => $merged, 'truncated' => $truncated];
    }

    /**
     * Assign a domain and gitea path to an update ticket.
     *
     * Creates a new helpdesk_updates record or updates an existing one.
     *
     * @return array{ok: bool, id: int|null, errors: array<string, string>}
     */
    public function assignUpdate(
        int $tenantId,
        string $ticketNo,
        string $debitorNo,
        string $debitorName,
        string $categoryCode,
        string $domainNo,
        string $domainUrl,
        string $giteaPath,
        string $notes,
        int $userId,
    ): array {
        $errors = [];

        $ticketNo = trim($ticketNo);
        if ($ticketNo === '') {
            $errors['ticket_no'] = t('Ticket number is required');
        }

        $debitorNo = trim($debitorNo);
        if ($debitorNo === '') {
            $errors['debitor_no'] = t('Debitor number is required');
        }

        $categoryCode = trim($categoryCode);
        if (!in_array($categoryCode, self::UPDATE_CATEGORIES, true)) {
            $errors['category_code'] = t('Invalid category');
        }

        if ($errors !== []) {
            return ['ok' => false, 'id' => null, 'errors' => $errors];
        }

        $domainNo = trim($domainNo);
        $domainUrl = trim($domainUrl);
        $giteaPath = trim($giteaPath);
        $notes = trim($notes);

        $status = ($domainNo !== '' || $giteaPath !== '') ? self::STATUS_ASSIGNED : self::STATUS_OPEN;

        $existing = $this->repository->findByTicketNo($tenantId, $ticketNo);

        if ($existing !== null) {
            $existingId = (int) ($existing['id'] ?? 0);
            if ($existingId <= 0) {
                return ['ok' => false, 'id' => null, 'errors' => ['general' => t('Failed to save')]];
            }

            $updated = $this->repository->updateAssignment($tenantId, $existingId, [
                'domain_no' => $domainNo,
                'domain_url' => $domainUrl,
                'gitea_path' => $giteaPath,
                'notes' => $notes,
                'status' => $status,
                'updated_by' => $userId,
            ]);

            if (!$updated) {
                return ['ok' => false, 'id' => null, 'errors' => ['general' => t('Failed to save')]];
            }

            return ['ok' => true, 'id' => $existingId, 'errors' => []];
        }

        $id = $this->repository->insert([
            'tenant_id' => $tenantId,
            'ticket_no' => $ticketNo,
            'category_code' => $categoryCode,
            'debitor_no' => $debitorNo,
            'debitor_name' => trim($debitorName),
            'domain_no' => $domainNo,
            'domain_url' => $domainUrl,
            'gitea_path' => $giteaPath,
            'notes' => $notes,
            'status' => $status,
            'created_by' => $userId,
        ]);

        if ($id === null) {
            return ['ok' => false, 'id' => null, 'errors' => ['general' => t('Failed to save')]];
        }

        return ['ok' => true, 'id' => $id, 'errors' => []];
    }

    public static function statusLabel(string $status): string
    {
        return match ($status) {
            self::STATUS_OPEN => t('Unassigned'),
            self::STATUS_ASSIGNED => t('Assigned'),
            default => $status,
        };
    }

    public static function statusVariant(string $status): string
    {
        return match ($status) {
            self::STATUS_OPEN => 'warning',
            self::STATUS_ASSIGNED => 'success',
            default => 'neutral',
        };
    }

    public static function ticketStateLabel(string $state): string
    {
        return match ($state) {
            'Offen' => t('Open'),
            'In Bearbeitung' => t('In progress'),
            'Erledigt' => t('Resolved'),
            'Geschlossen' => t('Closed'),
            default => $state !== '' ? $state : '—',
        };
    }

    public static function ticketStateVariant(string $state): string
    {
        return match ($state) {
            'Offen' => 'warning',
            'In Bearbeitung' => 'info',
            'Erledigt' => 'success',
            'Geschlossen' => 'neutral',
            default => 'neutral',
        };
    }

    public static function categoryLabel(string $code): string
    {
        return match ($code) {
            self::CATEGORY_UPDATE => t('Update'),
            self::CATEGORY_HOTFIX => t('Hotfix'),
            default => $code,
        };
    }

    public static function categoryVariant(string $code): string
    {
        return match ($code) {
            self::CATEGORY_UPDATE => 'neutral',
            self::CATEGORY_HOTFIX => 'warning',
            default => 'neutral',
        };
    }
}
