<?php
// ============================================================================
// receiver.php — Endpoint server-side pentru:
//   1. Primire XML eFactura (POST fără action) → salvare în temp/
//   2. Proxy ANAF APIs:
//      ?action=ping     — health check (no auth)
//      ?action=validate — proxy validare ANAF (anaf_token opțional → ruta OAuth; altfel ruta publică)
//      ?action=pdf      — proxy transformare XmlToPdf ANAF (PDF binary; anaf_token opțional)
//      ?action=cif      — lookup contribuabil după CIF (nu necesită token OAuth)
//   3. Curățare fișiere temporare (?cleanup=xml_XXXX.xml)
// ============================================================================

// === 1. Configurație ========================================================
// Citește config.json (fallback) și suprascrie cu variabile de mediu dacă sunt prezente.
// Util pentru deploy în container (Docker/Dokploy): nu trebuie rebuild la schimbare config.
//   ANAF_API_KEY        — suprascrie api_key
//   ANAF_ALLOWED_IPS    — listă IP-uri separate prin virgulă; "*" sau gol = check dezactivat
//   ANAF_TOKEN          — Bearer token OAuth ANAF (opțional, doar pentru viitoare extensii)
//   ANAF_TEMP_LIFETIME  — ore păstrare fișiere temp (default 1)
$configPath = dirname(__FILE__) . '/config.json';
$config = file_exists($configPath) ? json_decode(file_get_contents($configPath), true) : [];
if (!is_array($config)) $config = [];

if (($v = getenv('ANAF_API_KEY')) !== false && $v !== '')        $config['api_key'] = $v;
if (($v = getenv('ANAF_TOKEN'))   !== false && $v !== '')        $config['anaf_token'] = $v;
if (($v = getenv('ANAF_TEMP_LIFETIME')) !== false && $v !== '')  $config['temp_file_lifetime'] = intval($v);
if (($v = getenv('ANAF_ALLOWED_IPS')) !== false) {
    $config['allowed_ips'] = array_values(array_filter(array_map('trim', explode(',', $v))));
}

if (empty($config['api_key'])) {
    header('HTTP/1.1 500 Internal Server Error');
    header('Content-Type: application/json');
    die(json_encode(['success' => false, 'error' => 'Eroare la încărcarea configurației']));
}

// === 2. Funcții helper ======================================================

/** Validează structura XML și namespace-urile UBL. */
function validateXML($xmlContent) {
    libxml_use_internal_errors(true);
    $xmlContent = preg_replace('/^\xEF\xBB\xBF/', '', $xmlContent);
    $xmlContent = trim($xmlContent);
    $xml = simplexml_load_string($xmlContent);

    if ($xml === false) {
        $errors = libxml_get_errors();
        $msgs = array_map(function($e) {
            return [
                'level' => $e->level, 'code' => $e->code,
                'column' => $e->column, 'message' => $e->message, 'line' => $e->line
            ];
        }, $errors);
        libxml_clear_errors();
        return ['valid' => false, 'errors' => $msgs];
    }

    $namespaces = $xml->getNamespaces(true);
    $required = [
        'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2',
        'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2',
        'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2'
    ];
    foreach ($required as $ns) {
        if (!in_array($ns, array_values($namespaces))) {
            return ['valid' => false, 'errors' => [['message' => "Namespace lipsă: $ns"]]];
        }
    }
    return ['valid' => true, 'errors' => []];
}

/**
 * Verifică IP-ul clientului față de allowed_ips.
 * Returnează true dacă lista e goală sau conține "*" (check dezactivat — util în
 * container behind reverse proxy, unde REMOTE_ADDR e IP-ul intern Traefik/nginx).
 */
function checkIP() {
    global $config;
    $allowed = $config['allowed_ips'] ?? [];
    if (empty($allowed) || in_array('*', $allowed, true)) return true;
    return in_array($_SERVER['REMOTE_ADDR'], $allowed, true);
}

/** Verifică header-ul X-Api-Key față de api_key din config.json. */
function validateToken() {
    global $config;
    $headers = getallheaders();
    $token = $headers['X-Api-Key'] ?? '';
    return hash_equals($config['api_key'], $token);
}

/**
 * Execută un request cURL POST. Returnează ['body' => string, 'error' => string].
 * @param string $url     URL destinație
 * @param string $body    Corp request
 * @param array  $headers Headere suplimentare
 */
function curlPost($url, $body, $headers = []) {
    if (!function_exists('curl_init')) {
        return ['body' => '', 'error' => 'cURL nu este disponibil pe acest server'];
    }
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $body,
        CURLOPT_HTTPHEADER     => $headers,
        CURLOPT_TIMEOUT        => 30,
        CURLOPT_SSL_VERIFYPEER => true,
    ]);
    $resp  = curl_exec($ch);
    $error = curl_error($ch);
    curl_close($ch);
    return ['body' => $resp ?: '', 'error' => $error];
}

// === 3. Action routing (înainte de auth pentru ?action=ping) ================
$action = $_GET['action'] ?? '';

// Health check — nu necesită auth
if ($action === 'ping') {
    header('Content-Type: application/json');
    echo json_encode(['pong' => true]);
    exit;
}

// === 4. Auth ================================================================
// IP check: dacă allowed_ips e gol sau conține "*", se sare peste (vezi checkIP()).
if (!checkIP()) {
    header('HTTP/1.1 403 Forbidden');
    header('Content-Type: application/json');
    error_log("Acces interzis pentru IP: " . $_SERVER['REMOTE_ADDR']);
    die(json_encode(['success' => false, 'error' => 'Acces interzis', 'details' => 'IP-ul nu este autorizat']));
}

// ANAF proxy actions: nu necesită X-Api-Key (same-origin implicat prin IP check)
// Upload XML: necesită X-Api-Key
if (!in_array($action, ['validate', 'pdf', 'cif'])) {
    if ($_SERVER['REQUEST_METHOD'] !== 'GET' && !validateToken()) {
        header('HTTP/1.1 401 Unauthorized');
        header('Content-Type: application/json');
        error_log("Token invalid de la IP: " . $_SERVER['REMOTE_ADDR']);
        die(json_encode(['success' => false, 'error' => 'Token invalid', 'details' => 'Autentificare eșuată']));
    }
}

// === 5. ANAF proxy handlers =================================================

/**
 * Proxy validare ANAF eFactura.
 * Necesită: "anaf_token": "Bearer XXX" în config.json
 * POST https://api.anaf.ro/prod/FCTEL/rest/validare/FACT1
 */
function handleAnafValidate() {
    global $config;
    $xmlContent = file_get_contents('php://input');
    $token = $config['anaf_token'] ?? '';

    $headers = ['Content-Type: text/plain; charset=utf-8'];
    if ($token) {
        $headers[] = "Authorization: Bearer $token";
    }

    $result = curlPost('https://api.anaf.ro/prod/FCTEL/rest/validare/FACT1', $xmlContent, $headers);

    header('Content-Type: application/json');
    if ($result['error']) {
        http_response_code(502);
        echo json_encode(['error' => 'cURL error: ' . $result['error']]);
        exit;
    }
    // Transmite răspunsul ANAF direct — structura: {"Messages": [...]}
    echo $result['body'];
    exit;
}

/**
 * Proxy transformare ANAF XmlToPdf — întoarce PDF binary direct.
 * Doc: https://api.anaf.ro/prod/FCTEL/rest/transformare/{standard}/{novld}
 *   - standard = FACT1 (UBL Invoice / Credit Note)
 *   - novld absent → ANAF validează XML-ul; dacă invalid, întoarce JSON cu erori
 *   - novld = "DA" → skip validare (NEFOLOSIT aici — vrem validare implicită)
 *
 * Endpoint:
 *   - cu anaf_token configurat: api.anaf.ro (OAuth2)
 *   - fără token: webservicesp.anaf.ro (rută publică, fără auth)
 *
 * Răspuns:
 *   - PDF (application/pdf) la succes — body începe cu "%PDF-"
 *   - JSON cu erori validare la eșec (HTTP 200 sau 400)
 *
 * Frontend (anaf.js) detectează tipul după Content-Type și afișează corespunzător.
 */
function handleAnafPdf() {
    global $config;
    $xmlContent = file_get_contents('php://input');
    $token = $config['anaf_token'] ?? '';

    $headers = ['Content-Type: text/plain; charset=utf-8'];
    if ($token) {
        $headers[] = "Authorization: Bearer $token";
        $url = 'https://api.anaf.ro/prod/FCTEL/rest/transformare/FACT1';
    } else {
        $url = 'https://webservicesp.anaf.ro/prod/FCTEL/rest/transformare/FACT1';
    }

    $result = curlPost($url, $xmlContent, $headers);

    if ($result['error']) {
        header('Content-Type: application/json');
        http_response_code(502);
        echo json_encode(['error' => 'cURL error: ' . $result['error']]);
        exit;
    }

    $body = $result['body'];

    // Sniff response type — PDF binary începe cu "%PDF-", JSON cu "{".
    if (substr($body, 0, 5) === '%PDF-') {
        header('Content-Type: application/pdf');
        header('Content-Disposition: attachment; filename="factura_anaf.pdf"');
        echo $body;
        exit;
    }

    // JSON (erori validare) sau alt răspuns text — pasează către client.
    $trimmed = ltrim($body);
    if ($trimmed !== '' && ($trimmed[0] === '{' || $trimmed[0] === '[')) {
        header('Content-Type: application/json');
        http_response_code(400);
        echo $body;
        exit;
    }

    // Unknown response (HTML error page, etc.)
    header('Content-Type: application/json');
    http_response_code(502);
    echo json_encode(['error' => 'ANAF răspuns neașteptat', 'preview' => substr($body, 0, 200)]);
    exit;
}

/**
 * Proxy lookup contribuabil după CIF prin ANAF.
 * Nu necesită token OAuth — API public ANAF.
 *
 * API utilizat: PlatitorTvaRest v9 (sincron)
 *   POST https://webservicesp.anaf.ro/api/PlatitorTvaRest/v9/tva
 *   Body: [{"cui": <int>, "data": "YYYY-MM-DD"}]
 *   Răspuns direct: {found: [...], notFound: [...]}
 *   Doc: https://static.anaf.ro/static/10/Anaf/Informatii_R/Servicii_web/doc_WS_V9.txt
 *
 * Alternativă disponibilă: AsynchWebService v8 (async, batch până la 100 CUI-uri)
 *   Submit : POST https://webservicesp.anaf.ro/AsynchWebService/api/v8/ws/tva → correlationId
 *   Result : GET  https://webservicesp.anaf.ro/AsynchWebService/api/v7/ws/tva?id={correlationId}
 *            (după min. 2s; rezultat disponibil max. 3 zile)
 *   Doc: https://static.anaf.ro/static/10/Anaf/Informatii_R/Servicii_web/doc_WS_Async_V8.txt
 *   Potrivit pentru bulk lookup (ex. import multiplu CIF-uri); pentru single CIF la click
 *   user, v9 sincron e preferabil (un singur request, fără polling).
 */
function handleAnafCif() {
    $cif = intval($_GET['cif'] ?? '0');
    if ($cif <= 0) {
        header('Content-Type: application/json');
        http_response_code(400);
        echo json_encode(['error' => 'CIF invalid sau lipsă']);
        exit;
    }

    $today   = date('Y-m-d');
    $payload = json_encode([['cui' => $cif, 'data' => $today]]);
    $headers = ['Content-Type: application/json', 'Accept: application/json'];

    $result = curlPost(
        'https://webservicesp.anaf.ro/api/PlatitorTvaRest/v9/tva',
        $payload, $headers
    );

    header('Content-Type: application/json');

    if ($result['error']) {
        http_response_code(502);
        echo json_encode(['error' => 'ANAF indisponibil: ' . $result['error']]);
        exit;
    }

    $data = json_decode($result['body'], true);
    if (!$data || !array_key_exists('found', $data)) {
        http_response_code(502);
        echo json_encode(['error' => 'Răspuns neașteptat ANAF', 'raw' => $result['body']]);
        exit;
    }

    echo json_encode(_normalizeCifResponse($data, $cif));
    exit;
}

/** Normalizează răspunsul ANAF TVA v9 în formatul js/anaf.js. */
function _normalizeCifResponse($data, $cif) {
    $found = $data['found'] ?? [];
    if (empty($found)) {
        return ['found' => false];
    }
    $c  = $found[0];
    $dg = $c['date_generale']      ?? [];
    $as = $c['adresa_sediu_social'] ?? [];
    $tv = $c['inregistrare_scop_Tva'] ?? [];
    return [
        'found'         => true,
        'denumire'      => $dg['denumire'] ?? '',
        'adresa'        => $dg['adresa']   ?? '',
        'nrRegCom'      => $dg['nrRegCom'] ?? '',
        'cui'           => $dg['cui']      ?? $cif,
        'tvaActiv'      => !empty($tv['scpTVA']),
        'strada'        => trim(($as['sdenumire_Strada'] ?? '') . ' ' . ($as['snumar_Strada'] ?? '')),
        'oras'          => trim(preg_replace('/^(MUN\.|ORS\.|COM\.)\s+/iu', '', $as['sdenumire_Localitate'] ?? '')),
        'judetCod'      => 'RO-' . strtoupper($as['scod_JudetAuto'] ?? ''),
        'codPostal'     => $as['scod_Postal'] ?? '',
        'telefon'       => $dg['telefon'] ?? '',
        'statusEFactura'=> !empty($dg['statusRO_e_Factura']),
    ];
}

// === 6. Rutare acțiuni ANAF =================================================
switch ($action) {
    case 'validate': handleAnafValidate();
    case 'pdf':      handleAnafPdf();
    case 'cif':      handleAnafCif();
}

// === 7. Upload XML (comportament original) ==================================
$uploadDir = dirname(__FILE__) . '/temp/';
if (!file_exists($uploadDir)) {
    mkdir($uploadDir, 0777, true);
}

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    try {
        $xmlContent = file_get_contents('php://input');
        $validationResult = validateXML($xmlContent);

        if (!$validationResult['valid']) {
            header('Content-Type: application/json');
            http_response_code(400);
            echo json_encode(['success' => false, 'error' => 'XML invalid', 'details' => $validationResult['errors']]);
            error_log("Validare XML eșuată: " . json_encode($validationResult['errors']));
            exit;
        }

        $fileName = uniqid('xml_') . '.xml';
        $filePath = $uploadDir . $fileName;
        if (file_put_contents($filePath, $xmlContent)) {
            header('Content-Type: application/json');
            echo json_encode(['success' => true, 'fileName' => $fileName]);
        } else {
            throw new Exception('Eroare la salvarea fișierului');
        }
    } catch (Exception $e) {
        header('Content-Type: application/json');
        http_response_code(500);
        error_log("Eroare procesare XML: " . $e->getMessage());
        echo json_encode(['success' => false, 'error' => $e->getMessage()]);
    }
    exit;
}

// === 8. Curățare manuală fișiere temporare (?cleanup=xml_XXXX.xml) =========
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['cleanup'])) {
    $fileName = basename($_GET['cleanup']);
    if (preg_match('/^xml_[a-f0-9]+\.xml$/', $fileName)) {
        $filePath = $uploadDir . $fileName;
        if (file_exists($filePath)) {
            if (unlink($filePath)) {
                header('Content-Type: application/json');
                echo json_encode(['success' => true]);
            } else {
                header('Content-Type: application/json');
                http_response_code(500);
                error_log("Nu s-a putut șterge: " . $filePath);
                echo json_encode(['success' => false, 'error' => 'Nu s-a putut șterge fișierul']);
            }
        } else {
            header('Content-Type: application/json');
            http_response_code(404);
            echo json_encode(['success' => false, 'error' => 'Fișierul nu există']);
        }
    } else {
        header('Content-Type: application/json');
        http_response_code(400);
        error_log("Nume fișier invalid solicitat: " . $fileName);
        echo json_encode(['success' => false, 'error' => 'Nume fișier invalid']);
    }
    exit;
}

// === 9. Curățare automată fișiere vechi =====================================
$files  = glob($uploadDir . 'xml_*.xml');
$now    = time();
$maxAge = ($config['temp_file_lifetime'] ?? 1) * 3600;

foreach ($files as $file) {
    if ($now - filemtime($file) > $maxAge) {
        @unlink($file);
        error_log("Fișier vechi șters: " . basename($file));
    }
}
?>
