-- ====================================================================
-- P1-003: Package IMPORT_COMENZI pentru import comenzi web -> ROA
-- Sistem Import Comenzi Web -> ROA  
-- ====================================================================

-- Package pentru importul comenzilor web cu mapari complexe SKU -> CODMAT
CREATE OR REPLACE PACKAGE PACK_IMPORT_COMENZI AS

    -- Tipuri pentru returnarea rezultatelor
    TYPE t_articol_result IS RECORD (
        id_articol      NUMBER,
        codmat          VARCHAR2(50),
        cantitate_roa   NUMBER,
        pret_unitar     NUMBER,
        success         NUMBER,
        error_message   VARCHAR2(4000)
    );
    
    TYPE t_articol_table IS TABLE OF t_articol_result;
    
    -- Variabila package pentru ultima eroare (pentru orchestrator VFP)
    g_last_error VARCHAR2(4000);
    
    -- Functie pentru gasirea/maparea articolelor ROA
    FUNCTION gaseste_articol_roa(
        p_sku IN VARCHAR2,
        p_pret_web IN NUMBER DEFAULT NULL,
        p_cantitate_web IN NUMBER DEFAULT 1
    ) RETURN t_articol_table PIPELINED;
    
    -- Functie pentru importul complet al unei comenzi
    FUNCTION importa_comanda(
        p_nr_comanda_ext    IN VARCHAR2,
        p_data_comanda      IN DATE,
        p_id_partener       IN NUMBER,
        p_json_articole     IN CLOB,    -- JSON array cu articolele
        p_id_adresa_livrare IN NUMBER DEFAULT NULL,
        p_id_adresa_facturare IN NUMBER DEFAULT NULL,
        p_observatii        IN VARCHAR2 DEFAULT NULL
    ) RETURN NUMBER;  -- Returneaza ID_COMANDA sau -1 pentru eroare
    
    -- Functii pentru managementul erorilor (similar cu PACK_JSON)
    FUNCTION get_last_error RETURN VARCHAR2;
    PROCEDURE clear_error;

END PACK_IMPORT_COMENZI;
/

-- ====================================================================
-- Package Body - Implementarea functiilor
-- ====================================================================
CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_COMENZI AS

    -- Constante pentru configurare
    c_id_gestiune   CONSTANT NUMBER := NULL;  -- NULL pentru INTERNA=2 (comenzi client)  
    c_id_sectie     CONSTANT NUMBER := 2;    -- Prima sectie disponibilă
    c_id_pol        CONSTANT NUMBER := NULL;
    c_id_util       CONSTANT NUMBER := -3;  -- Sistem
    c_interna       CONSTANT NUMBER := 2;   -- Comenzi de la client (web)
    
    -- ================================================================
    -- Functii helper pentru managementul erorilor
    -- ================================================================
    FUNCTION get_last_error RETURN VARCHAR2 IS
    BEGIN
        RETURN g_last_error;
    END get_last_error;
    
    PROCEDURE clear_error IS
    BEGIN
        g_last_error := NULL;
    END clear_error;
    
    -- ================================================================
    -- Functii interne
    -- ================================================================
    
    -- Procedura interna pentru validarea seturilor
    FUNCTION valideaza_set(p_sku IN VARCHAR2) RETURN BOOLEAN IS
        v_suma_procent NUMBER := 0;
        v_count_articole NUMBER := 0;
    BEGIN
        SELECT NVL(SUM(procent_pret), 0), COUNT(*)
        INTO v_suma_procent, v_count_articole
        FROM articole_terti
        WHERE sku = p_sku 
          AND activ = 1;
          
        -- Validari logice pentru seturi
        IF v_count_articole > 1 THEN
            -- Set compus - suma procentelor trebuie sa fie intre 95-105% (toleranta)
            IF v_suma_procent < 95 OR v_suma_procent > 105 THEN
                -- pINFO('WARN VALIDEAZA_SET ' || p_sku || ': Suma procente nelogica: ' || v_suma_procent || '%', 'IMPORT_COMENZI');
                RETURN FALSE;
            END IF;
        ELSIF v_count_articole = 1 THEN
            -- Reimpachetare - procentul trebuie sa fie 100%
            IF v_suma_procent != 100 THEN
                -- pINFO('WARN VALIDEAZA_SET ' || p_sku || ': Reimpachetare cu procent != 100%: ' || v_suma_procent || '%', 'IMPORT_COMENZI');
                RETURN FALSE;
            END IF;
        END IF;
        
        RETURN TRUE;
    END valideaza_set;

    -- ================================================================
    -- Functia principala pentru gasirea articolelor ROA
    -- ================================================================
    FUNCTION gaseste_articol_roa(
        p_sku IN VARCHAR2,
        p_pret_web IN NUMBER DEFAULT NULL,
        p_cantitate_web IN NUMBER DEFAULT 1
    ) RETURN t_articol_table PIPELINED IS
    
        v_result t_articol_result;
        v_found_mapping BOOLEAN := FALSE;
        v_id_articol NUMBER;
        
        -- Cursor pentru maparile din ARTICOLE_TERTI
        CURSOR c_mapari IS
            SELECT at.codmat, at.cantitate_roa, at.procent_pret,
                   na.id_articol
            FROM articole_terti at
            JOIN nom_articole na ON na.codmat = at.codmat
            WHERE at.sku = p_sku 
              AND at.activ = 1
            ORDER BY at.procent_pret DESC;  -- Articolele principale primul
            
    BEGIN
        -- pINFO('GASESTE_ARTICOL ' || p_sku || ': Cautare articol pentru SKU: ' || p_sku, 'IMPORT_COMENZI');
        
        -- Initializare rezultat
        v_result.success := 0;
        v_result.error_message := NULL;
        
        -- STEP 1: Verifica maparile speciale din ARTICOLE_TERTI
        FOR rec IN c_mapari LOOP
            v_found_mapping := TRUE;
            
            v_result.id_articol := rec.id_articol;
            v_result.codmat := rec.codmat;
            v_result.cantitate_roa := rec.cantitate_roa * p_cantitate_web;
            
            -- Calculeaza pretul unitar pe baza procentului alocat
            IF p_pret_web IS NOT NULL THEN
                v_result.pret_unitar := (p_pret_web * rec.procent_pret / 100) / rec.cantitate_roa;
            ELSE
                -- Fara pret web, setam 0 (va fi necesar sa fie furnizat)
                v_result.pret_unitar := 0;
            END IF;
            
            v_result.success := 1;
            
            -- pINFO('GASESTE_ARTICOL ' || p_sku || ': Mapare gasita: ' || rec.codmat || 
                -- ', Cant: ' || v_result.cantitate_roa ||
                -- ', Pret: ' || v_result.pret_unitar, 'IMPORT_COMENZI');
                
            PIPE ROW(v_result);
        END LOOP;
        
        -- STEP 2: Daca nu s-au gasit mapari speciale, cauta direct in nom_articole
        IF NOT v_found_mapping THEN
            BEGIN
                SELECT id_articol, codmat
                INTO v_result.id_articol, v_result.codmat
                FROM nom_articole
                WHERE codmat = p_sku;
                
                v_result.cantitate_roa := p_cantitate_web;
                
                -- Pentru cautare directa, foloseste pretul din web daca este furnizat
                IF p_pret_web IS NOT NULL THEN
                    v_result.pret_unitar := p_pret_web;
                END IF;
                
                v_result.success := 1;
                
                -- pINFO('GASESTE_ARTICOL ' || p_sku || ': Gasit direct in nomenclator: ' || v_result.codmat, 'IMPORT_COMENZI');
                    
                PIPE ROW(v_result);
                
            EXCEPTION
                WHEN NO_DATA_FOUND THEN
                    v_result.success := 0;
                    v_result.error_message := 'SKU nu a fost gasit nici in ARTICOLE_TERTI, nici in nom_articole: ' || p_sku;
                    
                    -- pINFO('ERROR GASESTE_ARTICOL ' || p_sku || ': ' || v_result.error_message, 'IMPORT_COMENZI');
                    PIPE ROW(v_result);
                    
                WHEN TOO_MANY_ROWS THEN
                    v_result.success := 0;
                    v_result.error_message := 'Multiple articole gasite pentru SKU: ' || p_sku;
                    
                    -- pINFO('ERROR GASESTE_ARTICOL ' || p_sku || ': ' || v_result.error_message, 'IMPORT_COMENZI');
                    PIPE ROW(v_result);
            END;
        ELSE
            -- Valideaza seturile dupa ce au fost returnate toate maparile
            IF NOT valideaza_set(p_sku) THEN
                null;
                -- pINFO('WARN GASESTE_ARTICOL ' || p_sku || ': Set cu configuratie suspecta - verifica procentele', 'IMPORT_COMENZI');
            END IF;
        END IF;
        
    EXCEPTION
        WHEN OTHERS THEN
            v_result.success := 0;
            v_result.error_message := 'Eroare neasteptata: ' || SQLERRM;
            
            -- pINFO('ERROR GASESTE_ARTICOL ' || p_sku || ': Eroare neasteptata: ' || SQLERRM, 'IMPORT_COMENZI');
            PIPE ROW(v_result);
    END gaseste_articol_roa;

    -- ================================================================
    -- Functia pentru importul complet al unei comenzi
    -- ================================================================
    FUNCTION importa_comanda(
        p_nr_comanda_ext    IN VARCHAR2,
        p_data_comanda      IN DATE,
        p_id_partener       IN NUMBER,
        p_json_articole     IN CLOB,
        p_id_adresa_livrare IN NUMBER DEFAULT NULL,
        p_id_adresa_facturare IN NUMBER DEFAULT NULL,
        p_observatii        IN VARCHAR2 DEFAULT NULL
    ) RETURN NUMBER IS
    
        v_id_comanda NUMBER;
        v_data_livrare DATE;
        v_sku VARCHAR2(100);
        v_cantitate_web NUMBER;
        v_pret_web NUMBER;
        v_articole_procesate NUMBER := 0;
        v_articole_eroare NUMBER := 0;
        v_start_time DATE;
        v_json_pos NUMBER := 1;
        v_json_end NUMBER;
        v_json_item CLOB;
        
    BEGIN
        v_start_time := SYSDATE;
        
        -- Resetare eroare la inceputul procesarii
        clear_error;
        
        -- Validari de baza
        IF p_nr_comanda_ext IS NULL OR p_id_partener IS NULL THEN
            g_last_error := 'IMPORTA_COMANDA ' || NVL(p_nr_comanda_ext, 'NULL') || ': Parametri obligatorii lipsa';
            RETURN -1;
        END IF;
        
        -- Verifica daca comanda nu exista deja
        BEGIN
            SELECT id_comanda INTO v_id_comanda
            FROM comenzi
            WHERE comanda_externa = p_nr_comanda_ext
              AND sters = 0;
              
            -- pINFO('WARN IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Comanda exista deja cu ID: ' || v_id_comanda, 'IMPORT_COMENZI');
            RETURN v_id_comanda; -- Returneaza ID-ul comenzii existente
            
        EXCEPTION
            WHEN NO_DATA_FOUND THEN
                NULL; -- Normal, comanda nu exista
        END;
        
        -- Calculeaza data de livrare (comanda + 1 zi)
        v_data_livrare := p_data_comanda + 1;
        
        -- STEP 1: Creeaza comanda folosind versiunea overloaded cu OUT parameter
        BEGIN
            -- Apeleaza procedura adauga_comanda care returneaza ID_COMANDA prin OUT
            PACK_COMENZI.adauga_comanda(
                V_NR_COMANDA => p_nr_comanda_ext,
                V_DATA_COMANDA => p_data_comanda,
                V_ID => p_id_partener,                    -- ID_PART
                V_DATA_LIVRARE => v_data_livrare,
                V_PROC_DISCOUNT => 0,                     -- Fara discount implicit
                V_INTERNA => c_interna,
                V_ID_UTIL => c_id_util,
                V_ID_SECTIE => c_id_sectie,
                V_ID_ADRESA_FACTURARE => p_id_adresa_facturare,
                V_ID_ADRESA_LIVRARE => p_id_adresa_livrare,
                V_ID_CODCLIENT => NULL,                   -- Nu folosim cod client
                V_COMANDA_EXTERNA => p_nr_comanda_ext,
                V_ID_CTR => NULL,                         -- Nu avem contract
                V_ID_COMANDA => v_id_comanda              -- OUT parameter cu ID_COMANDA
            );
            
            IF v_id_comanda IS NULL OR v_id_comanda <= 0 THEN
                g_last_error := 'IMPORTA_COMANDA ' || p_nr_comanda_ext || ': PACK_COMENZI.adauga_comanda a returnat ID invalid';
                RETURN -1;
            END IF;
            
            -- pINFO('IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Comanda creata cu ID: ' || v_id_comanda, 'IMPORT_COMENZI');
                
        EXCEPTION
            WHEN OTHERS THEN
                g_last_error := 'IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Eroare la crearea comenzii: ' || SQLERRM;
                RETURN -1;
        END;
        
        -- STEP 2: Proceseaza articolele din JSON folosind PACK_JSON
        -- Asteapta format JSON: [{"sku":"ABC","cantitate":1,"pret":10.5},{"sku":"DEF","cantitate":2,"pret":20.0}]
        DECLARE
            v_articol_json VARCHAR2(4000);
            v_articol_count NUMBER := 0;
        BEGIN
            -- Parse JSON array folosind package-ul generic
            FOR json_obj IN (
                SELECT * FROM TABLE(PACK_JSON.parse_array(p_json_articole))
            ) LOOP
                v_articol_count := v_articol_count + 1;
                v_articol_json := json_obj.COLUMN_VALUE;
                
                BEGIN
                    -- Extrage datele folosind functiile PACK_JSON
                    v_sku := PACK_JSON.get_string(v_articol_json, 'sku');
                    v_cantitate_web := PACK_JSON.get_number(v_articol_json, 'cantitate');
                    v_pret_web := PACK_JSON.get_number(v_articol_json, 'pret');
                    
                    -- pINFO('IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Procesez articol ' || v_articol_count || ': ' || v_sku || ', cant: ' || v_cantitate_web || ', pret: ' || v_pret_web, 'IMPORT_COMENZI');
                    
                    -- STEP 3: Gaseste maparile pentru acest SKU
                    DECLARE
                        v_articole_table t_articol_table;
                        v_articol_idx NUMBER;
                    BEGIN
                        -- Apeleaza functia si stocheaza rezultatele
                        SELECT * BULK COLLECT INTO v_articole_table
                        FROM TABLE(gaseste_articol_roa(v_sku, v_pret_web, v_cantitate_web));
                        
                        -- Itereaza prin rezultate
                        IF v_articole_table.COUNT > 0 THEN
                            FOR v_articol_idx IN 1..v_articole_table.COUNT LOOP
                                DECLARE
                                    art_rec t_articol_result := v_articole_table(v_articol_idx);
                                BEGIN
                        IF art_rec.success = 1 THEN
                            -- Adauga articolul la comanda
                            BEGIN
                                PACK_COMENZI.adauga_articol_comanda(
                                    V_ID_COMANDA => v_id_comanda,
                                    V_ID_ARTICOL => art_rec.id_articol,
                                    V_ID_POL => c_id_pol,
                                    V_CANTITATE => art_rec.cantitate_roa,
                                    V_PRET => art_rec.pret_unitar,
                                    V_ID_UTIL => c_id_util,
                                    V_ID_SECTIE => c_id_sectie
                                );
                                
                                v_articole_procesate := v_articole_procesate + 1;
                                
                                -- pINFO('IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Articol adaugat: ' || art_rec.codmat || 
                                    -- ', cant: ' || art_rec.cantitate_roa ||
                                    -- ', pret: ' || art_rec.pret_unitar, 'IMPORT_COMENZI');
                                    
                            EXCEPTION
                                WHEN OTHERS THEN
                                    v_articole_eroare := v_articole_eroare + 1;
                                    -- pINFO('ERROR IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Eroare la adaugare articol ' || art_rec.codmat || ': ' || SQLERRM, 'IMPORT_COMENZI');
                            END;
                        ELSE
                            v_articole_eroare := v_articole_eroare + 1;
                            -- pINFO('ERROR IMPORTA_COMANDA ' || p_nr_comanda_ext || ': SKU nu a putut fi mapat: ' || v_sku || ' - ' || art_rec.error_message, 'IMPORT_COMENZI');
                        END IF;
                                END; -- End art_rec DECLARE block
                            END LOOP; -- End v_articol_idx loop
                        ELSE
                            v_articole_eroare := v_articole_eroare + 1;
                            -- pINFO('WARN IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Niciun articol gasit pentru SKU: ' || v_sku, 'IMPORT_COMENZI');
                        END IF;
                    END; -- End DECLARE block pentru v_articole_table
                    
                EXCEPTION
                    WHEN OTHERS THEN
                        v_articole_eroare := v_articole_eroare + 1;
                        -- pINFO('ERROR IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Eroare la procesarea articolului ' || v_articol_count || ': ' || SQLERRM, 'IMPORT_COMENZI');
                END;
                
            END LOOP;
            
        EXCEPTION
            WHEN OTHERS THEN
                g_last_error := 'IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Eroare la parsarea JSON: ' || SQLERRM;
                RETURN -1;
        END;
        
        -- Verifica daca s-au procesat articole cu succes
        IF v_articole_procesate = 0 THEN
            g_last_error := 'IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Niciun articol nu a fost procesat cu succes';
            RETURN -1;
        END IF;
        
        -- Log sumar final
        -- pINFO('IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Import finalizat - ID comanda: ' || v_id_comanda || 
            -- ', Articole procesate: ' || v_articole_procesate ||
            -- ', Articole cu erori: ' || v_articole_eroare ||
            -- ', Timp procesare: ' || ROUND((SYSDATE - v_start_time) * 24 * 60 * 60, 2) || 's', 'IMPORT_COMENZI');
        
        RETURN v_id_comanda;
        
    EXCEPTION
        WHEN OTHERS THEN
            g_last_error := 'IMPORTA_COMANDA ' || p_nr_comanda_ext || ': Eroare neasteptata in importa_comanda: ' || SQLERRM;
            RETURN -1;
    END importa_comanda;

END PACK_IMPORT_COMENZI;
/

-- ====================================================================
-- Grant-uri pentru utilizarea package-ului
-- ====================================================================
-- GRANT EXECUTE ON PACK_IMPORT_COMENZI TO PUBLIC;

