<?php

declare(strict_types=1);

final class HyroLeadWorkflowService
{
    public function __construct(
        private readonly PDO $database,
        private readonly HyroLeadActivityRepository $activities
    ) {
    }

    public function update(
        int $leadId,
        array $input,
        array $adminUser
    ): array {
        $allowedStatuses = [
            'new',
            'contacted',
            'demo_scheduled',
            'trial_started',
            'customer',
            'closed',
        ];

        $status = strtolower(
            trim((string) ($input['status'] ?? ''))
        );

        $assignedTo = trim(
            (string) ($input['assigned_to'] ?? '')
        );

        $internalNote = trim(
            (string) ($input['internal_note'] ?? '')
        );

        $errors = [];

        if (!in_array($status, $allowedStatuses, true)) {
            $errors['status'] = 'Please select a valid lead status.';
        }

        if (mb_strlen($assignedTo) > 100) {
            $errors['assigned_to'] =
                'Assigned person name is too long.';
        }

        if (mb_strlen($internalNote) > 3000) {
            $errors['internal_note'] =
                'Internal note must not exceed 3000 characters.';
        }

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

        $statement = $this->database->prepare(
            '
            SELECT
                id,
                status,
                assigned_to
            FROM hyro_leads
            WHERE id = :id
            LIMIT 1
            '
        );

        $statement->execute([
            'id' => $leadId,
        ]);

        $lead = $statement->fetch();

        if (!is_array($lead)) {
            return [
                'success' => false,
                'errors' => [
                    'general' => 'Lead not found.',
                ],
            ];
        }

        $oldStatus = (string) ($lead['status'] ?? 'new');

        $oldAssignedTo = trim(
            (string) ($lead['assigned_to'] ?? '')
        );

        $newAssignedTo = $assignedTo === ''
            ? null
            : $assignedTo;

        $now = date('Y-m-d H:i:s');

        $this->database->beginTransaction();

        try {
            $update = $this->database->prepare(
                '
                UPDATE hyro_leads
                SET
                    status = :status,
                    assigned_to = :assigned_to,
                    updated_at = :updated_at
                WHERE id = :id
                '
            );

            $update->execute([
                'status' => $status,
                'assigned_to' => $newAssignedTo,
                'updated_at' => $now,
                'id' => $leadId,
            ]);

            $adminId = isset($adminUser['id'])
                ? (int) $adminUser['id']
                : null;

            $adminName = trim(
                (string) (
                    $adminUser['full_name']
                    ?? 'Administrator'
                )
            );

            if ($status !== $oldStatus) {
                $this->activities->create(
                    $leadId,
                    'status_changed',
                    'Lead status changed',
                    sprintf(
                        'Status changed from %s to %s.',
                        $this->label($oldStatus),
                        $this->label($status)
                    ),
                    $oldStatus,
                    $status,
                    $adminId,
                    $adminName
                );
            }

            if ($assignedTo !== $oldAssignedTo) {
                $this->activities->create(
                    $leadId,
                    'assignment_changed',
                    'Lead assignment updated',
                    $assignedTo === ''
                        ? 'Lead assignment was removed.'
                        : 'Lead assigned to ' . $assignedTo . '.',
                    $oldAssignedTo === ''
                        ? null
                        : $oldAssignedTo,
                    $newAssignedTo,
                    $adminId,
                    $adminName
                );
            }

            if ($internalNote !== '') {
                $this->activities->create(
                    $leadId,
                    'internal_note',
                    'Internal note added',
                    $internalNote,
                    null,
                    null,
                    $adminId,
                    $adminName
                );
            }

            $this->database->commit();

            hyro_log(
                'leads',
                'Lead workflow updated.',
                [
                    'lead_id' => $leadId,
                    'status' => $status,
                    'assigned_to' => $newAssignedTo,
                    'updated_by' => $adminName,
                ]
            );

            return [
                'success' => true,
                'errors' => [],
            ];
        } catch (Throwable $exception) {
            if ($this->database->inTransaction()) {
                $this->database->rollBack();
            }

            hyro_log(
                'leads',
                'Lead workflow update failed.',
                [
                    'lead_id' => $leadId,
                    'error' => $exception->getMessage(),
                ]
            );

            return [
                'success' => false,
                'errors' => [
                    'general' =>
                        'Unable to update the lead. Please try again.',
                ],
            ];
        }
    }

    private function label(string $value): string
    {
        return ucwords(
            str_replace('_', ' ', $value)
        );
    }
}
