<?php

require_once __DIR__ . '/../../core/database.php';
require_once __DIR__ . '/FactoClient.php';
require_once __DIR__ . '/FactoFileStorageService.php';
require_once __DIR__ . '/../billing/BillingSupplierResolverService.php';

class FactoInboxDocumentsSyncService
{
    private $pdo;
    private $client;
    private $storage;
    private $environment;

    public function __construct($environment = null)
    {
        $this->pdo = db();
        $this->client = new FactoClient($environment);
        $this->storage = new FactoFileStorageService();
        $this->environment = $this->client->getEnvironment();
    }

    public function syncInboxDocuments($perPage = 100, $params = array(), $fetchFiles = false)
    {
        $page = 1;
        $totalSaved = 0;
        $perPage = max(1, min(100, (int) $perPage));

        do {
            $query = array_merge($params, array(
                'page' => $page,
                'per_page' => $perPage
            ));

            $response = $this->client->getInboxDocuments($query);
            $documents = $this->extractEmbeddedList($response, 'inbox_documents');

            foreach ($documents as $document) {
                $saved = $this->saveInboxDocument($document);
                if ($saved) {
                    $totalSaved++;
                    if ($fetchFiles) {
                        $inboxId = $this->pick($document, 'inbox_document_id');
                        if ($inboxId) {
                            $this->refreshInboxDocument($inboxId, true);
                        }
                    }
                }
            }

            $pageCount = isset($response['page_count']) ? (int) $response['page_count'] : 1;
            if ($pageCount < 1) {
                $pageCount = 1;
            }
            $page++;
        } while ($page <= $pageCount);

        $this->log('success', 'Inbox FACTO sincronizado ambiente ' . $this->environment . ': ' . $totalSaved . ' documentos recibidos.');

        return $totalSaved;
    }

    public function refreshInboxDocument($inboxDocumentId, $saveFiles = true)
    {
        $document = $this->client->getInboxDocument($inboxDocumentId);
        $savedPaths = array();

        if ($saveFiles) {
            $electronicDocument = $this->extractElectronicDocument($document);
            if (!empty($electronicDocument)) {
                $savedPaths = $this->storage->saveElectronicDocumentFiles((string) $inboxDocumentId, $electronicDocument, $this->environment . '/inbox');
                $document['_icylab_saved_files'] = $savedPaths;
            }
        }

        $this->saveInboxDocument($document, $savedPaths);
        return $document;
    }

    public function acceptInboxDocument($inboxDocumentId, $payload = array())
    {
        if (empty($payload)) {
            $payload = array('document_approval_status' => 'accepted');
        }

        return $this->client->patchInboxDocument($inboxDocumentId, $payload);
    }

    public function rejectInboxDocument($inboxDocumentId, $payload = array())
    {
        if (empty($payload)) {
            $payload = array('document_approval_status' => 'rejected');
        }

        return $this->client->patchInboxDocument($inboxDocumentId, $payload);
    }

    public function saveInboxDocument($d, $savedPaths = array())
    {
        $inboxDocumentId = $this->pick($d, 'inbox_document_id');

        if (!$inboxDocumentId) {
            return false;
        }

        $pdfFilePath = isset($savedPaths['pdf_file_path']) ? $savedPaths['pdf_file_path'] : $this->pickNested($d, array('_icylab_saved_files', 'pdf_file_path'));
        $xmlFilePath = isset($savedPaths['xml_file_path']) ? $savedPaths['xml_file_path'] : $this->pickNested($d, array('_icylab_saved_files', 'xml_file_path'));
        $rawPayload = json_encode($d, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

        $stmt = $this->pdo->prepare("\n            INSERT INTO facto_inbox_documents\n            (\n                environment,\n                inbox_document_id,\n                document_id,\n                account_id,\n                email_subject,\n                sender_email,\n                document_date,\n                receive_date,\n                inbox_document_status,\n                issuer_name,\n                issuer_tax_id_code,\n                issuer_tax_id_type,\n                receiver_tax_id_code,\n                receiver_tax_id_type,\n                document_type_id,\n                document_type_taxbureau,\n                document_number,\n                purchase_order,\n                reception_confirmation_status,\n                reception_confirmation_date,\n                document_approval_status,\n                document_approval_date,\n                product_receipt_status,\n                product_receipt_date,\n                product_receipt_location,\n                net_amount,\n                taxes_amount,\n                total_amount,\n                pdf_file_path,\n                xml_file_path,\n                raw_payload,\n                sync_status,\n                last_synced_at\n            )\n            VALUES\n            (\n                ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'synced', NOW()\n            )\n            ON DUPLICATE KEY UPDATE\n                environment = VALUES(environment),\n                document_id = VALUES(document_id),\n                account_id = VALUES(account_id),\n                email_subject = VALUES(email_subject),\n                sender_email = VALUES(sender_email),\n                document_date = VALUES(document_date),\n                receive_date = VALUES(receive_date),\n                inbox_document_status = VALUES(inbox_document_status),\n                issuer_name = VALUES(issuer_name),\n                issuer_tax_id_code = VALUES(issuer_tax_id_code),\n                issuer_tax_id_type = VALUES(issuer_tax_id_type),\n                receiver_tax_id_code = VALUES(receiver_tax_id_code),\n                receiver_tax_id_type = VALUES(receiver_tax_id_type),\n                document_type_id = VALUES(document_type_id),\n                document_type_taxbureau = VALUES(document_type_taxbureau),\n                document_number = VALUES(document_number),\n                purchase_order = VALUES(purchase_order),\n                reception_confirmation_status = VALUES(reception_confirmation_status),\n                reception_confirmation_date = VALUES(reception_confirmation_date),\n                document_approval_status = VALUES(document_approval_status),\n                document_approval_date = VALUES(document_approval_date),\n                product_receipt_status = VALUES(product_receipt_status),\n                product_receipt_date = VALUES(product_receipt_date),\n                product_receipt_location = VALUES(product_receipt_location),\n                net_amount = VALUES(net_amount),\n                taxes_amount = VALUES(taxes_amount),\n                total_amount = VALUES(total_amount),\n                pdf_file_path = COALESCE(VALUES(pdf_file_path), pdf_file_path),\n                xml_file_path = COALESCE(VALUES(xml_file_path), xml_file_path),\n                raw_payload = VALUES(raw_payload),\n                sync_status = 'synced',\n                last_synced_at = NOW(),\n                updated_at = CURRENT_TIMESTAMP\n        ");

        $stmt->execute(array(
            $this->environment,
            $inboxDocumentId,
            $this->pick($d, 'document_id'),
            $this->pick($d, 'account_id'),
            $this->pick($d, 'email_subject'),
            $this->pick($d, 'sender_email'),
            $this->normalizeDate($this->pick($d, 'document_date')),
            $this->normalizeDateTime($this->pick($d, 'receive_date')),
            $this->pick($d, 'inbox_document_status'),
            $this->pick($d, 'issuer_name'),
            $this->pick($d, 'issuer_tax_id_code'),
            $this->pick($d, 'issuer_tax_id_type'),
            $this->pick($d, 'receiver_tax_id_code'),
            $this->pick($d, 'receiver_tax_id_type'),
            $this->toInt($this->pick($d, 'document_type_id')),
            $this->pick($d, 'document_type_taxbureau'),
            $this->pick($d, 'document_number'),
            $this->pick($d, 'purchase_order'),
            $this->pick($d, 'reception_confirmation_status'),
            $this->normalizeDateTime($this->pick($d, 'reception_confirmation_date')),
            $this->pick($d, 'document_approval_status'),
            $this->normalizeDateTime($this->pick($d, 'document_approval_date')),
            $this->pick($d, 'product_receipt_status'),
            $this->normalizeDateTime($this->pick($d, 'product_receipt_date')),
            $this->pick($d, 'product_receipt_location'),
            $this->toDecimal($this->pick($d, 'net_amount')),
            $this->toDecimal($this->pick($d, 'taxes_amount')),
            $this->toDecimal($this->pick($d, 'total_amount')),
            $pdfFilePath,
            $xmlFilePath,
            $rawPayload
        ));

        try {
            if (class_exists('BillingSupplierResolverService')) {
                $resolver = new BillingSupplierResolverService($this->pdo);
                $resolver->resolveDocumentByInboxId($inboxDocumentId, $this->environment, true);
            }
        } catch (Throwable $e) {
            // No bloquear sincronizacion FACTO por resolucion automatica de proveedor.
        }

        return true;
    }

    private function extractEmbeddedList($response, $key)
    {
        if (isset($response['_embedded'][$key]) && is_array($response['_embedded'][$key])) {
            return $response['_embedded'][$key];
        }

        if (isset($response[$key]) && is_array($response[$key])) {
            return $response[$key];
        }

        return array();
    }

    private function extractElectronicDocument($document)
    {
        if (isset($document['electronic_document']) && is_array($document['electronic_document'])) {
            return $document['electronic_document'];
        }

        return array();
    }

    private function pick($array, $key)
    {
        return isset($array[$key]) && $array[$key] !== '' ? $array[$key] : null;
    }

    private function pickNested($array, $path)
    {
        $cursor = $array;
        foreach ($path as $key) {
            if (!is_array($cursor) || !array_key_exists($key, $cursor)) {
                return null;
            }
            $cursor = $cursor[$key];
        }

        return $cursor !== '' ? $cursor : null;
    }

    private function normalizeDate($value)
    {
        if (!$value) {
            return null;
        }

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

    private function normalizeDateTime($value)
    {
        if (!$value) {
            return null;
        }

        $timestamp = strtotime($value);
        return $timestamp ? date('Y-m-d H:i:s', $timestamp) : null;
    }

    private function toInt($value)
    {
        if ($value === null || $value === '') {
            return null;
        }

        return (int) $value;
    }

    private function toDecimal($value)
    {
        if ($value === null || $value === '') {
            return 0;
        }

        return (float) str_replace(',', '.', (string) $value);
    }

    private function log($status, $message)
    {
        $stmt = $this->pdo->prepare("\n            INSERT INTO facto_sync_logs (status, message)\n            VALUES (?, ?)\n        ");

        $stmt->execute(array($status, $message));
    }
}
