<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\Database;
use App\Models\CustomerModel;
use App\Models\LeadModel;

/**
 * LeadService
 *
 * Owns the two operations that are more than a plain CRUD call:
 * converting a won lead into a real customer record, and merging two
 * duplicate leads into one survivor. Both touch multiple tables inside
 * a transaction — exactly the kind of logic that does NOT belong
 * spread across a controller and a model.
 */
final class LeadService
{
    public function __construct(
        private readonly LeadModel $leads = new LeadModel(),
        private readonly CustomerModel $customers = new CustomerModel()
    ) {
    }

    /** Converts a lead into a customer record, carrying over contact info and linking back via source_lead_id. */
    public function convertToCustomer(int $leadId, array $customerData, int $actingUserId): int
    {
        $lead = $this->leads->findWithRelations($leadId);
        if ($lead === null) {
            throw new \RuntimeException('Lead not found.');
        }

        return Database::transaction(function () use ($lead, $customerData, $actingUserId) {
            $customerId = $this->customers->insert([
                'business_line_id' => $lead['business_line_id'],
                'company_id' => $lead['company_id'],
                'source_lead_id' => $lead['id'],
                'service_plan_id' => $customerData['service_plan_id'] ?? null,
                'assigned_staff_id' => $customerData['assigned_staff_id'] ?? null,
                'first_name' => $lead['first_name'],
                'last_name' => $lead['last_name'],
                'email' => $lead['email'],
                'phone' => $lead['phone'],
                'address_line1' => $customerData['address_line1'] ?? null,
                'city' => $customerData['city'] ?? null,
                'province' => $customerData['province'] ?? null,
                'postal_code' => $customerData['postal_code'] ?? null,
                'status' => 'active',
                'currency' => $lead['currency'],
                'customer_since' => date('Y-m-d'),
            ], $actingUserId);

            $this->leads->update($lead['id'], ['converted_customer_id' => $customerId], $actingUserId);

            ActivityLogger::record(
                'lead',
                $lead['id'],
                'lead_converted',
                'Lead converted to customer',
                ['customer_id' => $customerId]
            );
            ActivityLogger::record(
                'customer',
                $customerId,
                'customer_created_from_lead',
                'Customer created from a converted lead',
                ['lead_id' => $lead['id']]
            );

            return $customerId;
        });
    }

    /**
     * Merges $duplicateLeadId INTO $survivorLeadId: every appointment,
     * note, comment, attachment, and tag that pointed at the duplicate
     * now points at the survivor; the duplicate is soft-deleted and
     * flagged via merged_into_lead_id (never hard-deleted, so the
     * audit trail and any external reference to its id still resolves).
     */
    public function merge(int $survivorLeadId, int $duplicateLeadId, int $actingUserId): void
    {
        if ($survivorLeadId === $duplicateLeadId) {
            throw new \InvalidArgumentException('Cannot merge a lead into itself.');
        }

        $pdo = Database::connection();

        Database::transaction(function () use ($pdo, $survivorLeadId, $duplicateLeadId, $actingUserId) {
            // Re-point every polymorphic and direct reference.
            $pdo->prepare("UPDATE appointments SET lead_id = :survivor WHERE lead_id = :dup AND deleted_at IS NULL")
                ->execute(['survivor' => $survivorLeadId, 'dup' => $duplicateLeadId]);

            foreach (['notes', 'comments', 'attachments'] as $table) {
                $pdo->prepare("UPDATE {$table} SET entity_id = :survivor WHERE entity_type = 'lead' AND entity_id = :dup")
                    ->execute(['survivor' => $survivorLeadId, 'dup' => $duplicateLeadId]);
            }

            $pdo->prepare("UPDATE taggables SET entity_id = :survivor WHERE entity_type = 'lead' AND entity_id = :dup")
                ->execute(['survivor' => $survivorLeadId, 'dup' => $duplicateLeadId]);

            // Tasks belong directly to the lead (lead_tasks.lead_id).
            $pdo->prepare('UPDATE lead_tasks SET lead_id = :survivor WHERE lead_id = :dup')
                ->execute(['survivor' => $survivorLeadId, 'dup' => $duplicateLeadId]);

            // Carry over the activity timeline for continuity.
            $pdo->prepare("UPDATE activities SET entity_id = :survivor WHERE entity_type = 'lead' AND entity_id = :dup")
                ->execute(['survivor' => $survivorLeadId, 'dup' => $duplicateLeadId]);

            // Mark the duplicate as merged-away and soft-delete it.
            $pdo->prepare(
                'UPDATE leads SET merged_into_lead_id = :survivor, deleted_at = NOW(), updated_by = :user WHERE id = :dup'
            )->execute(['survivor' => $survivorLeadId, 'user' => $actingUserId, 'dup' => $duplicateLeadId]);

            ActivityLogger::record(
                'lead',
                $survivorLeadId,
                'lead_merged',
                "Merged lead #{$duplicateLeadId} into this record",
                ['merged_lead_id' => $duplicateLeadId],
                category: 'admin'
            );
        });
    }
}
