<?php

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

class FactoDocumentsSyncService
{
    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 syncIssuedDocuments($fromDate, $toDate, $perPage = 100, $fetchFiles = false)
    {
        // FACTO GET /documents: received_issued_flag 0 = issued, 1 = received.
        return $this->syncDocumentsByFlag(0, $fromDate, $toDate, $perPage, $fetchFiles);
    }

    public function syncReceivedDocuments($fromDate, $toDate, $perPage = 100, $fetchFiles = false)
    {
        // FACTO GET /documents: received_issued_flag 0 = issued, 1 = received.
        return $this->syncDocumentsByFlag(1, $fromDate, $toDate, $perPage, $fetchFiles);
    }

    public function syncDocumentsByFlag($receivedIssuedFlag, $fromDate, $toDate, $perPage = 100, $fetchFiles = false)
    {
        $page = 1;
        $totalSaved = 0;
        $perPage = max(1, min(100, (int) $perPage));
        $receivedIssuedFlag = (int) $receivedIssuedFlag;

        do {
            $response = $this->client->getDocuments(array(
                'page' => $page,
                'per_page' => $perPage,
                'issue_date_from' => $fromDate,
                'issue_date_to' => $toDate,
                'received_issued_flag' => $receivedIssuedFlag,
                'order_by' => 'desc'
            ));

            $documents = $this->extractEmbeddedList($response, 'documents');

            foreach ($documents as $document) {
                $saved = $this->saveDocument($document);
                if ($saved) {
                    $totalSaved++;

                    if ($fetchFiles) {
                        $documentId = $this->pick($document, 'document_id');
                        if ($documentId) {
                            $this->refreshDocument($documentId, true);
                        }
                    }
                }
            }

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

        $kind = $receivedIssuedFlag === 0 ? 'emitidos' : 'recibidos';
        $this->log('success', 'Documentos FACTO ' . $kind . ' sincronizados ambiente ' . $this->environment . ': ' . $totalSaved . ' desde ' . $fromDate . ' hasta ' . $toDate);

        return $totalSaved;
    }

    public function refreshDocument($documentId, $saveFiles = true)
    {
        $document = $this->client->getDocument($documentId, true);
        $savedPaths = array();

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

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

    public function saveDocument($d, $savedPaths = array())
    {
        $documentId = $this->pick($d, 'document_id');

        if (!$documentId) {
            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);
        $payloadHash = hash('sha256', $rawPayload ?: (string) $documentId);

        $stmt = $this->pdo->prepare("\n            INSERT INTO facto_documents\n            (\n                environment,\n                document_id,\n                account_id,\n                issue_date,\n                document_number,\n                document_type_id,\n                issuer_tax_id_code,\n                receiver_legal_name,\n                receiver_tax_id_code,\n                customer_supplier_id,\n                document_status,\n                currency_id,\n                received_issued_flag,\n                net_amount,\n                taxes_amount,\n                total_amount,\n                document_type_taxbureau,\n                taxbureau_sending_status,\n                taxbureau_validation_status,\n                document_sequence_code,\n                pdf_file_path,\n                xml_file_path,\n                raw_payload,\n                payload_hash,\n                last_synced_at\n            )\n            VALUES\n            (\n                ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW()\n            )\n            ON DUPLICATE KEY UPDATE\n                environment = VALUES(environment),\n                account_id = VALUES(account_id),\n                issue_date = VALUES(issue_date),\n                document_number = VALUES(document_number),\n                document_type_id = VALUES(document_type_id),\n                issuer_tax_id_code = VALUES(issuer_tax_id_code),\n                receiver_legal_name = VALUES(receiver_legal_name),\n                receiver_tax_id_code = VALUES(receiver_tax_id_code),\n                customer_supplier_id = VALUES(customer_supplier_id),\n                document_status = VALUES(document_status),\n                currency_id = VALUES(currency_id),\n                received_issued_flag = VALUES(received_issued_flag),\n                net_amount = VALUES(net_amount),\n                taxes_amount = VALUES(taxes_amount),\n                total_amount = VALUES(total_amount),\n                document_type_taxbureau = VALUES(document_type_taxbureau),\n                taxbureau_sending_status = VALUES(taxbureau_sending_status),\n                taxbureau_validation_status = VALUES(taxbureau_validation_status),\n                document_sequence_code = VALUES(document_sequence_code),\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                payload_hash = VALUES(payload_hash),\n                last_synced_at = NOW(),\n                updated_at = CURRENT_TIMESTAMP\n        ");

        $stmt->execute(array(
            $this->environment,
            $documentId,
            $this->pick($d, 'account_id'),
            $this->normalizeDate($this->pick($d, 'issue_date')),
            $this->pick($d, 'document_number'),
            $this->pick($d, 'document_type_id'),
            $this->pick($d, 'issuer_tax_id_code'),
            $this->pick($d, 'receiver_legal_name'),
            $this->pick($d, 'receiver_tax_id_code'),
            $this->pick($d, 'customer_supplier_id'),
            $this->toInt($this->pick($d, 'document_status')),
            $this->pick($d, 'currency_id'),
            $this->toInt($this->pick($d, 'received_issued_flag')),
            $this->toDecimal($this->pick($d, 'net_amount')),
            $this->toDecimal($this->pick($d, 'taxes_amount')),
            $this->toDecimal($this->pick($d, 'total_amount')),
            $this->pick($d, 'document_type_taxbureau'),
            $this->pick($d, 'taxbureau_sending_status'),
            $this->pick($d, 'taxbureau_validation_status'),
            $this->pick($d, 'document_sequence_code'),
            $pdfFilePath,
            $xmlFilePath,
            $rawPayload,
            $payloadHash
        ));

        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 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));
    }
}
