<?php

/**
 * ICYLAB OS - TimelineAuditService
 *
 * Centraliza la construccion de entradas limpias para crm_activities.
 * Regla base: registrar solo cambios reales y evitar ruido de campos no modificados.
 */
class TimelineAuditService
{
    private PDO $pdo;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    public function insertActivity($opportunityId, $customerId, string $customerName, string $activityType, string $title, array $changes, string $ownerName, string $intro = ''): bool
    {
        $lines = array();
        $intro = trim($intro);
        if ($intro !== '') {
            $lines[] = $intro;
        }

        foreach ($changes as $change) {
            $change = trim((string)$change);
            if ($change !== '') {
                $lines[] = $change;
            }
        }

        if (empty($lines)) {
            return false;
        }

        $stmt = $this->pdo->prepare("\n            INSERT INTO crm_activities\n            (opportunity_id, customer_id, customer_name, activity_type, title, description, owner_name, activity_date, created_at)\n            VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())\n        ");

        $stmt->execute(array(
            $opportunityId ?: null,
            $customerId ?: null,
            $customerName,
            $activityType,
            $title,
            implode("\n", $lines),
            $ownerName
        ));

        return true;
    }

    public function buildQuoteCreatedChanges(array $quote, array $items): array
    {
        $subtotal = (float)($quote['subtotal'] ?? 0);
        $discountAmount = (float)($quote['discount_amount'] ?? 0);
        $net = $subtotal - $discountAmount;
        $total = (float)($quote['total'] ?? 0);

        $changes = array();
        $changes[] = 'Estado inicial: ' . $this->displayValue($quote['status'] ?? 'Borrador');
        $changes[] = 'Monto neto inicial: ' . $this->money($net);
        $changes[] = 'Total facturable inicial: ' . $this->money($total);
        $changes[] = 'Productos: ' . $this->itemsSummary($items);

        if (!empty($quote['invoice_issue_date'])) {
            $changes[] = 'Emision factura: ' . $this->date($quote['invoice_issue_date']);
        }

        if (!empty($quote['payment_date'])) {
            $changes[] = 'Fecha de pago: ' . $this->date($quote['payment_date']);
        }

        return $changes;
    }

    public function buildQuoteUpdateChanges(array $oldQuote, array $newQuote, array $oldItems, array $newItems): array
    {
        $changes = array();

        $this->addTextChange($changes, 'Estado', $oldQuote['status'] ?? '', $newQuote['status'] ?? '');
        $this->addPercentChange($changes, 'Descuento', $oldQuote['discount_percent'] ?? 0, $newQuote['discount_percent'] ?? 0);
        $this->addMoneyChange($changes, 'Subtotal', $oldQuote['subtotal'] ?? 0, $newQuote['subtotal'] ?? 0);
        $this->addMoneyChange($changes, 'Monto neto', $this->quoteNet($oldQuote), $this->quoteNet($newQuote));
        $this->addMoneyChange($changes, 'Total facturable', $oldQuote['total'] ?? 0, $newQuote['total'] ?? 0);
        $this->addDateChange($changes, 'Emision factura', $oldQuote['invoice_issue_date'] ?? null, $newQuote['invoice_issue_date'] ?? null);
        $this->addDateChange($changes, 'Fecha de pago', $oldQuote['payment_date'] ?? null, $newQuote['payment_date'] ?? null);
        $this->addNotesChange($changes, $oldQuote['notes'] ?? '', $newQuote['notes'] ?? '');

        if (!$this->sameItems($oldItems, $newItems)) {
            $changes[] = 'Productos: ' . $this->itemsSummary($oldItems) . ' -> ' . $this->itemsSummary($newItems);
        }

        return $changes;
    }

    public function buildOpportunityUpdateChanges(array $old, array $new): array
    {
        $changes = array();

        $this->addTextChange($changes, 'Cliente', $old['customer_name'] ?? '', $new['customer_name'] ?? '');
        $this->addTextChange($changes, 'Titulo', $old['title'] ?? '', $new['title'] ?? '');
        $this->addTextChange($changes, 'Etapa', $old['stage'] ?? '', $new['stage'] ?? '');
        $this->addMoneyChange($changes, 'Valor esperado', $old['expected_value'] ?? 0, $new['expected_value'] ?? 0);
        $this->addPercentChange($changes, 'Probabilidad', $old['probability'] ?? 0, $new['probability'] ?? 0);
        $this->addTextChange($changes, 'Responsable', $old['owner_name'] ?? '', $new['owner_name'] ?? '');
        $this->addTextChange($changes, 'Origen', $old['source'] ?? '', $new['source'] ?? '');
        $this->addTextChange($changes, 'Competidor', $old['competitor'] ?? '', $new['competitor'] ?? '');
        $this->addTextChange($changes, 'Proxima accion', $old['next_action'] ?? '', $new['next_action'] ?? '');
        $this->addDateChange($changes, 'Fecha proxima accion', $old['next_action_date'] ?? null, $new['next_action_date'] ?? null);
        $this->addDateChange($changes, 'Fecha estimada de cierre', $old['expected_close_date'] ?? null, $new['expected_close_date'] ?? null);
        $this->addTextChange($changes, 'Cobranza', $old['payment_status'] ?? '', $new['payment_status'] ?? '');
        $this->addDateChange($changes, 'Vencimiento de pago', $old['payment_due_date'] ?? null, $new['payment_due_date'] ?? null);
        $this->addDateChange($changes, 'Fecha de pago', $old['payment_paid_date'] ?? null, $new['payment_paid_date'] ?? null);
        $this->addNotesChange($changes, $old['notes'] ?? '', $new['notes'] ?? '');

        return $changes;
    }

    public function addTextChange(array &$changes, string $label, $oldValue, $newValue): void
    {
        $old = $this->normalizeText($oldValue);
        $new = $this->normalizeText($newValue);

        if ($old === $new) {
            return;
        }

        $changes[] = $label . ': ' . $this->displayValue($old) . ' -> ' . $this->displayValue($new);
    }

    public function addMoneyChange(array &$changes, string $label, $oldValue, $newValue): void
    {
        $old = round((float)$oldValue, 2);
        $new = round((float)$newValue, 2);

        if ($old === $new) {
            return;
        }

        $changes[] = $label . ': ' . $this->money($old) . ' -> ' . $this->money($new);
    }

    public function addPercentChange(array &$changes, string $label, $oldValue, $newValue): void
    {
        $old = round((float)$oldValue, 2);
        $new = round((float)$newValue, 2);

        if ($old === $new) {
            return;
        }

        $changes[] = $label . ': ' . $this->percent($old) . ' -> ' . $this->percent($new);
    }

    public function addDateChange(array &$changes, string $label, $oldValue, $newValue): void
    {
        $old = $this->normalizeDate($oldValue);
        $new = $this->normalizeDate($newValue);

        if ($old === $new) {
            return;
        }

        $changes[] = $label . ': ' . $this->displayDate($old) . ' -> ' . $this->displayDate($new);
    }

    public function addNotesChange(array &$changes, $oldNotes, $newNotes): void
    {
        $old = $this->normalizeText($oldNotes);
        $new = $this->normalizeText($newNotes);

        if ($old === $new) {
            return;
        }

        if ($old === '' && $new !== '') {
            $changes[] = 'Notas: añadidas';
            return;
        }

        if ($old !== '' && $new === '') {
            $changes[] = 'Notas: eliminadas';
            return;
        }

        $changes[] = 'Notas: actualizadas';
    }

    public function quoteNet(array $quote): float
    {
        return (float)($quote['subtotal'] ?? 0) - (float)($quote['discount_amount'] ?? 0);
    }

    public function sameItems(array $oldItems, array $newItems): bool
    {
        return $this->itemsFingerprint($oldItems) === $this->itemsFingerprint($newItems);
    }

    public function itemsSummary(array $items): string
    {
        $lines = count($items);
        $units = 0;
        $net = 0.0;

        $unitsByLabel = array();

        foreach ($items as $item) {
            $qty = (float)($item['quantity'] ?? 0);
            $unit = $this->normalizeText($item['unit'] ?? 'unidad');
            if ($unit === '') {
                $unit = 'unidad';
            }
            $units += $qty;
            if (!isset($unitsByLabel[$unit])) {
                $unitsByLabel[$unit] = 0;
            }
            $unitsByLabel[$unit] += $qty;
            $net += (float)($item['total_item'] ?? (($item['quantity'] ?? 0) * ($item['price_unit'] ?? 0)));
        }

        $unitParts = array();
        foreach ($unitsByLabel as $unit => $qty) {
            $unitParts[] = $this->formatQuantity((float)$qty) . ' ' . $unit;
        }
        $unitSummary = !empty($unitParts) ? implode(', ', $unitParts) : $this->formatQuantity($units) . ' unidad';

        return $lines . ' linea' . ($lines === 1 ? '' : 's') . ' / ' . $unitSummary . ' / ' . $this->money($net);
    }

    public function money($value): string
    {
        return '$' . number_format((float)$value, 0, ',', '.');
    }

    public function percent($value): string
    {
        $value = (float)$value;
        if (abs($value - round($value)) < 0.005) {
            return number_format($value, 0, ',', '.') . '%';
        }
        return number_format($value, 2, ',', '.') . '%';
    }

    public function date($value): string
    {
        $normalized = $this->normalizeDate($value);
        return $this->displayDate($normalized);
    }

    public function normalizeDate($value): string
    {
        $value = trim((string)$value);
        if ($value === '' || $value === '0000-00-00') {
            return '';
        }

        $ts = strtotime($value);
        return $ts ? date('Y-m-d', $ts) : $value;
    }

    public function displayDate($value): string
    {
        $value = trim((string)$value);
        if ($value === '') {
            return 'Sin definir';
        }

        $ts = strtotime($value);
        return $ts ? date('d-m-Y', $ts) : $value;
    }

    public function displayValue($value): string
    {
        $value = trim((string)$value);
        return $value === '' ? 'Sin definir' : $value;
    }

    private function normalizeText($value): string
    {
        $value = trim((string)$value);
        $value = preg_replace('/\s+/u', ' ', $value);
        return $value === null ? '' : $value;
    }

    private function itemsFingerprint(array $items): string
    {
        $normalized = array();

        foreach ($items as $item) {
            $normalized[] = array(
                'product_id' => (string)($item['product_id'] ?? ''),
                'name' => $this->normalizeText($item['name'] ?? ($item['display_name'] ?? '')),
                'description' => $this->normalizeText($item['description'] ?? ''),
                'quantity' => round((float)($item['quantity'] ?? 0), 4),
                'unit' => $this->normalizeText($item['unit'] ?? 'unidad'),
                'price_unit' => round((float)($item['price_unit'] ?? 0), 2),
                'total_item' => round((float)($item['total_item'] ?? (($item['quantity'] ?? 0) * ($item['price_unit'] ?? 0))), 2),
            );
        }

        return json_encode($normalized, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }

    private function formatQuantity(float $value): string
    {
        if (abs($value - round($value)) < 0.0001) {
            return number_format($value, 0, ',', '.');
        }

        return rtrim(rtrim(number_format($value, 2, ',', '.'), '0'), ',');
    }
}
