<?php
require_once __DIR__ . '/db.php';
require_once __DIR__ . '/nextcloud.php';
require_once __DIR__ . '/contract_file_helper.php';

class EmailSyncManager {
    private $pdo;
    private $imap_conn;
    private $settings;

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

    private function loadSettings() {
        $stmt = $this->pdo->query("SELECT setting_key, setting_value FROM system_settings");
        $this->settings = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
    }

    public function getImapConn() {
        return $this->imap_conn;
    }

    public function connect() {
        if (empty($this->settings['email_imap_host'])) return false;

        $host = $this->settings['email_imap_host'];
        $port = $this->settings['email_imap_port'] ?: '993';
        $encryption = $this->settings['email_imap_encryption'] ?: 'ssl';
        $user = $this->settings['email_imap_username'];
        $pass = $this->settings['email_imap_password'];
        $folder = $this->settings['email_imap_folder'] ?: 'INBOX';

        $connectionString = "{" . $host . ":" . $port . "/imap/" . $encryption . "/novalidate-cert}" . $folder;
        
        try {
            $this->imap_conn = imap_open($connectionString, $user, $pass);
            if (!$this->imap_conn) {
                $errors = imap_errors();
                error_log("IMAP Connection Error: " . ($errors ? implode(', ', $errors) : 'Unknown error'));
                return false;
            }
            return true;
        } catch (Exception $e) {
            error_log("IMAP Exception: " . $e->getMessage());
            return false;
        }
    }

    public function sync($limit = 50) {
        if (!$this->imap_conn) return ['success' => false, 'error' => 'Não conectado ao IMAP'];

        // Buscar todos os emails (limitando para não sobrecarregar)
        $emails = imap_search($this->imap_conn, 'ALL');
        if ($emails) {
            rsort($emails); // Mais recentes primeiro
            $emails = array_slice($emails, 0, $limit);
        }
        
        $new_messages = 0;
        $skipped_no_contract = 0;

        if ($emails) {
            foreach ($emails as $email_number) {
                $overview = imap_fetch_overview($this->imap_conn, $email_number, 0);
                if (empty($overview)) continue;
                
                $header = imap_headerinfo($this->imap_conn, $email_number);
                $message_id = $header->message_id ?? '';
                $subject = $overview[0]->subject ?? 'Sem assunto';
                
                // Decodificar assunto se necessário
                $decoded_subject = "";
                $elements = imap_mime_header_decode($subject);
                foreach ($elements as $element) {
                    $decoded_subject .= $element->text;
                }
                $subject = $decoded_subject ?: $subject;

                $from = $header->from[0]->mailbox . "@" . $header->from[0]->host;
                $from_name = "";
                if (isset($header->from[0]->personal)) {
                    $elements = imap_mime_header_decode($header->from[0]->personal);
                    foreach ($elements as $element) {
                        $from_name .= $element->text;
                    }
                }
                $from_name = $from_name ?: $from;
                
                $date = date('Y-m-d H:i:s', $overview[0]->udate);

                // Verificar se a mensagem já foi processada e vinculada
                $check = $this->pdo->prepare("SELECT id, contract_id FROM email_contract_messages WHERE message_id_header = ?");
                $check->execute([$message_id]);
                $existing = $check->fetch();
                
                // Se já existe, verificar se já tem anexos processados
                if ($existing && $existing['contract_id']) {
                    $contract_id = $existing['contract_id'];
                    $msg_id = $existing['id'];
                    
                    // Se o corpo atual parece ter lixo MIME, vamos atualizar
                    $stmtCheck = $this->pdo->prepare("SELECT body_text FROM email_contract_messages WHERE id = ?");
                    $stmtCheck->execute([$msg_id]);
                    $currentBody = $stmtCheck->fetchColumn();
                    if (strpos($currentBody, '--_') !== false || strpos($currentBody, 'Content-Type:') !== false) {
                        $newBody = $this->getBody($email_number);
                        $this->pdo->prepare("UPDATE email_contract_messages SET body_text = ? WHERE id = ?")->execute([$newBody, $msg_id]);
                    }

                    $att_check = $this->pdo->prepare("SELECT COUNT(*) FROM email_contract_attachments WHERE message_id = ?");
                    $att_check->execute([$msg_id]);
                    if ($att_check->fetchColumn() > 0) {
                        continue; // Já tem contrato e anexos, pular
                    }
                } else {
                    // Lógica normal de novo contrato/vínculo
                    $clean_subject = trim(str_ireplace(['Re:', 'Fwd:', 'Enc:', 'RES:', 'ENC:', 'RESP:'], '', $subject));
                    
                    $stmt = $this->pdo->prepare("
                        SELECT id FROM email_contracts 
                        WHERE sender_email = ? 
                        AND (email_subject LIKE ? OR ? LIKE CONCAT('%', email_subject, '%'))
                        LIMIT 1
                    ");
                    $stmt->execute([$from, "%$clean_subject%", $clean_subject]);
                    $contract = $stmt->fetch();

                    $contract_id = $contract ? $contract['id'] : null;

                    if (!$contract_id) {
                        if (strpos($from, 'cpanel') !== false || strpos($from, 'no-reply') !== false || strpos($from, 'noreply') !== false) {
                            $skipped_no_contract++;
                            continue;
                        }

                        $attachments = $this->getAttachments($email_number);
                        $has_pdf = false;
                        foreach ($attachments as $at) {
                            if (strtolower(pathinfo($at['name'], PATHINFO_EXTENSION)) === 'pdf') {
                                $has_pdf = true;
                                break;
                            }
                        }

                        $stmt = $this->pdo->prepare("INSERT INTO email_contracts (company_name, sender_email, email_subject, unit_name, status) VALUES (?, ?, ?, 'Geral', 'Recebido')");
                        $stmt->execute([$from_name, $from, $clean_subject]);
                        $contract_id = $this->pdo->lastInsertId();
                    }

                    if ($existing) {
                        $stmtM = $this->pdo->prepare("UPDATE email_contract_messages SET contract_id = ?, body_text = ? WHERE id = ?");
                        $stmtM->execute([$contract_id, $this->getBody($email_number), $existing['id']]);
                        $msg_id = $existing['id'];
                    } else {
                        $body = $this->getBody($email_number);
                        $stmtM = $this->pdo->prepare("INSERT INTO email_contract_messages (contract_id, message_id_header, direction, body_text, sender_name, sender_email, sent_at) VALUES (?, ?, 'incoming', ?, ?, ?, ?)");
                        $stmtM->execute([$contract_id, $message_id, $body, $from_name, $from, $date]);
                        $msg_id = $this->pdo->lastInsertId();
                    }
                }

                // Processar anexos (seja mensagem nova ou re-processamento de anexos faltantes)
                $attachments = $this->getAttachments($email_number);
                foreach ($attachments as $at) {
                    $fileName = time() . '_' . $at['name'];
                    
                    // Buscar unidade para pasta
                    $stmtU = $this->pdo->prepare("SELECT unit_name FROM email_contracts WHERE id = ?");
                    $stmtU->execute([$contract_id]);
                    $unit_name = $stmtU->fetchColumn() ?: 'Geral';
                    $unitFolder = preg_replace('/[\/:*?"<>|]/', '_', $unit_name);
                    
                    $destRemotePath = cfh_build_remote_contract_path($unitFolder, $fileName);
                    
                    try {
                        $tmp = tempnam(sys_get_temp_dir(), 'email_att');
                        file_put_contents($tmp, $at['content']);
                        
                        $uploadSuccess = false;
                        $storedPath = $destRemotePath;
                        try {
                            $nc = new Nextcloud();
                            $remoteRoot = trim(str_replace('\\', '/', (string)cfh_get_remote_contracts_root()), '/');
                            $nc->createFolder($remoteRoot . '/' . $unitFolder);
                            if ($nc->uploadFile($tmp, $destRemotePath)) {
                                $uploadSuccess = true;
                            }
                        } catch (Exception $ncEx) {
                            error_log("Nextcloud upload failed: " . $ncEx->getMessage());
                        }

                        // Se o Nextcloud falhou ou não está configurado, salvar LOCALMENTE no diretório de backup
                        // Isso garante que o arquivo apareça na interface via serve_file.php
                        if (!$uploadSuccess) {
                            $localBackupPath = cfh_get_backup_absolute_path($destRemotePath, basename($destRemotePath));
                            $localBackupDir = dirname($localBackupPath);
                            if (!is_dir($localBackupDir)) @mkdir($localBackupDir, 0777, true);
                            if (@file_put_contents($localBackupPath, $at['content'])) {
                                $uploadSuccess = true;
                                $storedPath = str_replace('\\', '/', cfh_get_backup_relative_path($destRemotePath, basename($destRemotePath)));
                            }
                        }

                        if ($uploadSuccess) {
                            $stmtA = $this->pdo->prepare("INSERT INTO email_contract_attachments (contract_id, message_id, original_name, stored_path, file_ext, mime_type, file_size) VALUES (?, ?, ?, ?, ?, ?, ?)");
                            $stmtA->execute([$contract_id, $msg_id, $at['name'], $storedPath, pathinfo($at['name'], PATHINFO_EXTENSION), $at['type'], strlen($at['content'])]);
                            
                            // Se for PDF, marcar como uma versão do contrato automaticamente
                            if (strtolower(pathinfo($at['name'], PATHINFO_EXTENSION)) === 'pdf') {
                                $stmtVN = $this->pdo->prepare("SELECT MAX(version_number) FROM email_contract_versions WHERE contract_id = ?");
                                $stmtVN->execute([$contract_id]);
                                $next_v = (int)$stmtVN->fetchColumn() + 1;

                                $stmtV = $this->pdo->prepare("INSERT INTO email_contract_versions (contract_id, version_number, version_label, file_path, file_name, created_by) VALUES (?, ?, ?, ?, ?, ?)");
                                $stmtV->execute([$contract_id, $next_v, 'v'.$next_v, $storedPath, $at['name'], null]);
                            }
                        }
                        unlink($tmp);
                    } catch (Exception $e) {
                        error_log("Erro ao salvar anexo de e-mail: " . $e->getMessage());
                    }
                }

                $this->pdo->prepare("UPDATE email_contracts SET last_message_at = NOW() WHERE id = ?")->execute([$contract_id]);
                $new_messages++;
            }
        }

        return [
            'success' => true, 
            'new_messages' => $new_messages, 
            'skipped_no_contract' => $skipped_no_contract
        ];
    }

    private function getBody($uid) {
        $structure = imap_fetchstructure($this->imap_conn, $uid);
        
        // Tenta encontrar HTML primeiro, depois Plain Text
        $body = $this->getRecursivePart($uid, $structure, "HTML");
        if (!$body) {
            $body = $this->getRecursivePart($uid, $structure, "PLAIN");
        }
        
        return $body ? strip_tags($body) : "Mensagem sem conteúdo de texto.";
    }

    private function getRecursivePart($uid, $structure, $subtype, $partNum = "") {
        if ($structure->type == 0 && strtoupper($structure->subtype) == strtoupper($subtype)) {
            return $this->getPart($uid, $partNum ?: "1");
        }

        if (isset($structure->parts) && count($structure->parts)) {
            foreach ($structure->parts as $index => $subpart) {
                $newPartNum = ($partNum === "") ? ($index + 1) : ($partNum . "." . ($index + 1));
                $result = $this->getRecursivePart($uid, $subpart, $subtype, $newPartNum);
                if ($result) return $result;
            }
        }
        return null;
    }

    private function getPart($uid, $partNum) {
        $data = imap_fetchbody($this->imap_conn, $uid, $partNum);
        $structure = imap_fetchstructure($this->imap_conn, $uid);
        
        if ($partNum == "" || $partNum == "0") return $this->decodeBody($data, $structure->encoding);

        $part = $structure;
        $parts = explode(".", $partNum);
        foreach ($parts as $p) {
            if (isset($part->parts[$p - 1])) {
                $part = $part->parts[$p - 1];
            }
        }

        return $this->decodeBody($data, $part->encoding);
    }

    private function decodeBody($data, $encoding) {
        if ($encoding == 3) return imap_base64($data);
        if ($encoding == 4) return imap_qprint($data);
        return $data;
    }

    private function getAttachments($uid) {
        $attachments = [];
        $structure = imap_fetchstructure($this->imap_conn, $uid);

        if (isset($structure->parts) && count($structure->parts)) {
            foreach ($structure->parts as $index => $part) {
                $attachments = array_merge($attachments, $this->extractAttachments($uid, $part, $index + 1));
            }
        }
        return $attachments;
    }

    private function extractAttachments($uid, $part, $partNum) {
        $attachments = [];
        
        // Determinar se é um anexo
        $is_attachment = false;
        $filename = "";

        // 1. Verificar disposição
        if ($part->ifdisposition) {
            $disposition = strtolower($part->disposition);
            if ($disposition == 'attachment' || $disposition == 'inline') {
                $is_attachment = true;
            }
        }

        // 2. Procurar nome do arquivo em dparameters
        if ($part->ifdparameters) {
            foreach ($part->dparameters as $object) {
                if (strtolower($object->attribute) == 'filename' || strtolower($object->attribute) == 'name') {
                    $filename = $this->decodeHeader($object->value);
                    $is_attachment = true;
                }
            }
        }

        // 3. Procurar nome do arquivo em parameters
        if (!$filename && $part->ifparameters) {
            foreach ($part->parameters as $object) {
                if (strtolower($object->attribute) == 'name' || strtolower($object->attribute) == 'filename') {
                    $filename = $this->decodeHeader($object->value);
                    $is_attachment = true;
                }
            }
        }

        // Se identificamos como anexo e temos um nome
        if ($is_attachment && $filename) {
            $attachments[] = [
                'name' => $filename,
                'content' => $this->decodeBody(imap_fetchbody($this->imap_conn, $uid, $partNum), $part->encoding),
                'type' => $part->subtype
            ];
        }

        // Recursividade para partes aninhadas
        if (isset($part->parts)) {
            foreach ($part->parts as $index => $subpart) {
                $attachments = array_merge($attachments, $this->extractAttachments($uid, $subpart, $partNum . "." . ($index + 1)));
            }
        }

        return $attachments;
    }

    private function decodeHeader($text) {
        $decoded = "";
        $elements = imap_mime_header_decode($text);
        foreach ($elements as $element) {
            $decoded .= $element->text;
        }
        return $decoded ?: $text;
    }

    public function __destruct() {
        if ($this->imap_conn) imap_close($this->imap_conn);
    }
}
