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

-- Package pentru importul comenzilor web cu mapări complexe SKU → CODMAT
CREATE OR REPLACE PACKAGE 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;
    
    -- Funcție pentru găsirea/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;
    
    -- Funcție pentru importul complet al unei comenzi web
    FUNCTION importa_comanda_web(
        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_observatii        IN VARCHAR2 DEFAULT NULL
    ) RETURN NUMBER;  -- Returnează ID_COMANDA sau -1 pentru eroare

END IMPORT_COMENZI;
/

-- ====================================================================
-- Package Body - Implementarea funcțiilor
-- ====================================================================
CREATE OR REPLACE PACKAGE BODY IMPORT_COMENZI AS

    -- Constante pentru configurare
    c_id_gestiune   CONSTANT NUMBER := 1;
    c_id_sectie     CONSTANT NUMBER := 1;
    c_id_pol        CONSTANT NUMBER := NULL;
    c_id_util       CONSTANT NUMBER := -3;  -- Sistem
    c_interna       CONSTANT NUMBER := 0;   -- Externe
    
    -- Procedură internă pentru logging
    PROCEDURE log_operation(
        p_level IN VARCHAR2,           -- INFO, WARN, ERROR
        p_operation IN VARCHAR2,       -- GASESTE_ARTICOL, IMPORTA_COMANDA
        p_reference IN VARCHAR2,       -- SKU sau Nr_Comanda
        p_message IN VARCHAR2,
        p_details IN VARCHAR2 DEFAULT NULL
    ) IS
        PRAGMA AUTONOMOUS_TRANSACTION;
    BEGIN
        -- Log în tabel sau fișier sistem (simplificat pentru acest exemplu)
        INSERT INTO import_log (
            log_time, log_level, operation, reference_id, message, details
        ) VALUES (
            SYSDATE, p_level, p_operation, p_reference, p_message, p_details
        );
        COMMIT;
    EXCEPTION
        WHEN OTHERS THEN
            -- Fallback: Nu bloca operația principală dacă logging-ul eșuează
            NULL;
    END log_operation;
    
    -- Procedură internă 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;
          
        -- Validări logice pentru seturi
        IF v_count_articole > 1 THEN
            -- Set compus - suma procentelor trebuie să fie între 95-105% (toleranță)
            IF v_suma_procent < 95 OR v_suma_procent > 105 THEN
                log_operation('WARN', 'VALIDEAZA_SET', p_sku, 
                    'Suma procente nelogică: ' || v_suma_procent || '%');
                RETURN FALSE;
            END IF;
        ELSIF v_count_articole = 1 THEN
            -- Reîmpachetare - procentul trebuie să fie 100%
            IF v_suma_procent != 100 THEN
                log_operation('WARN', 'VALIDEAZA_SET', p_sku,
                    'Reîmpachetare cu procent != 100%: ' || v_suma_procent || '%');
                RETURN FALSE;
            END IF;
        END IF;
        
        RETURN TRUE;
    END valideaza_set;

    -- ================================================================
    -- Funcția principală pentru găsirea 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 mapările din ARTICOLE_TERTI
        CURSOR c_mapari IS
            SELECT at.codmat, at.cantitate_roa, at.procent_pret,
                   na.id_articol, na.pret_vanzare
            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
        log_operation('INFO', 'GASESTE_ARTICOL', p_sku, 
            'Căutare articol pentru SKU: ' || p_sku);
        
        -- Inițializare rezultat
        v_result.success := 0;
        v_result.error_message := NULL;
        
        -- STEP 1: Verifică mapările 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;
            
            -- Calculează prețul 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
                -- Folosește prețul din nomenclator
                v_result.pret_unitar := NVL(rec.pret_vanzare, 0);
            END IF;
            
            v_result.success := 1;
            
            log_operation('INFO', 'GASESTE_ARTICOL', p_sku,
                'Mapare găsită: ' || rec.codmat || 
                ', Cant: ' || v_result.cantitate_roa ||
                ', Preț: ' || v_result.pret_unitar);
                
            PIPE ROW(v_result);
        END LOOP;
        
        -- STEP 2: Dacă nu s-au găsit mapări speciale, caută direct în nom_articole
        IF NOT v_found_mapping THEN
            BEGIN
                SELECT id_articol, codmat, NVL(pret_vanzare, 0)
                INTO v_result.id_articol, v_result.codmat, v_result.pret_unitar
                FROM nom_articole
                WHERE codmat = p_sku
                  AND activ = 1;
                
                v_result.cantitate_roa := p_cantitate_web;
                
                -- Pentru căutare directă, folosește prețul din web dacă este furnizat
                IF p_pret_web IS NOT NULL THEN
                    v_result.pret_unitar := p_pret_web;
                END IF;
                
                v_result.success := 1;
                
                log_operation('INFO', 'GASESTE_ARTICOL', p_sku,
                    'Găsit direct în nomenclator: ' || v_result.codmat);
                    
                PIPE ROW(v_result);
                
            EXCEPTION
                WHEN NO_DATA_FOUND THEN
                    v_result.success := 0;
                    v_result.error_message := 'SKU nu a fost găsit nici în ARTICOLE_TERTI, nici în nom_articole: ' || p_sku;
                    
                    log_operation('ERROR', 'GASESTE_ARTICOL', p_sku, v_result.error_message);
                    PIPE ROW(v_result);
                    
                WHEN TOO_MANY_ROWS THEN
                    v_result.success := 0;
                    v_result.error_message := 'Multiple articole găsite pentru SKU: ' || p_sku;
                    
                    log_operation('ERROR', 'GASESTE_ARTICOL', p_sku, v_result.error_message);
                    PIPE ROW(v_result);
            END;
        ELSE
            -- Validează seturile după ce au fost returnate toate mapările
            IF NOT valideaza_set(p_sku) THEN
                log_operation('WARN', 'GASESTE_ARTICOL', p_sku,
                    'Set cu configurație suspectă - verifică procentele');
            END IF;
        END IF;
        
    EXCEPTION
        WHEN OTHERS THEN
            v_result.success := 0;
            v_result.error_message := 'Eroare neașteptată: ' || SQLERRM;
            
            log_operation('ERROR', 'GASESTE_ARTICOL', p_sku, 
                'Eroare neașteptată', SQLERRM);
            PIPE ROW(v_result);
    END gaseste_articol_roa;

    -- ================================================================
    -- Funcția pentru importul complet al unei comenzi web
    -- ================================================================
    FUNCTION importa_comanda_web(
        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_observatii        IN VARCHAR2 DEFAULT NULL
    ) RETURN NUMBER IS
    
        v_id_comanda NUMBER;
        v_data_livrare DATE;
        v_json_obj JSON_OBJECT_T;
        v_json_array JSON_ARRAY_T;
        v_articol_obj JSON_OBJECT_T;
        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;
        
    BEGIN
        v_start_time := SYSDATE;
        
        log_operation('INFO', 'IMPORTA_COMANDA', p_nr_comanda_ext,
            'Începere import comandă pentru partener: ' || p_id_partener);
        
        -- Validări de bază
        IF p_nr_comanda_ext IS NULL OR p_id_partener IS NULL THEN
            log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
                'Parametri obligatorii lipsă');
            RETURN -1;
        END IF;
        
        -- Verifică dacă comanda nu există deja
        BEGIN
            SELECT id_comanda INTO v_id_comanda
            FROM comenzi
            WHERE comanda_externa = p_nr_comanda_ext
              AND sters = 0;
              
            log_operation('WARN', 'IMPORTA_COMANDA', p_nr_comanda_ext,
                'Comanda există deja cu ID: ' || v_id_comanda);
            RETURN v_id_comanda; -- Returnează ID-ul comenzii existente
            
        EXCEPTION
            WHEN NO_DATA_FOUND THEN
                NULL; -- Normal, comanda nu există
        END;
        
        -- Calculează data de livrare (comanda + 1 zi)
        v_data_livrare := p_data_comanda + 1;
        
        -- STEP 1: Creează comanda folosind package-ul existent
        BEGIN
            v_id_comanda := PACK_COMENZI.adauga_comanda(
                p_nr_comanda => p_nr_comanda_ext,
                p_data_comanda => p_data_comanda,
                p_id_partener => p_id_partener,
                p_data_livrare => v_data_livrare,
                p_id_gestiune => c_id_gestiune,
                p_id_sectie => c_id_sectie,
                p_interna => c_interna,
                p_id_util => c_id_util,
                p_comanda_externa => p_nr_comanda_ext,
                p_id_adresa_livrare => p_id_adresa_livrare,
                p_observatii => p_observatii
            );
            
            IF v_id_comanda IS NULL OR v_id_comanda <= 0 THEN
                log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
                    'PACK_COMENZI.adauga_comanda a returnat ID invalid');
                RETURN -1;
            END IF;
            
            log_operation('INFO', 'IMPORTA_COMANDA', p_nr_comanda_ext,
                'Comanda creată cu ID: ' || v_id_comanda);
                
        EXCEPTION
            WHEN OTHERS THEN
                log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
                    'Eroare la crearea comenzii', SQLERRM);
                RETURN -1;
        END;
        
        -- STEP 2: Procesează articolele din JSON
        BEGIN
            v_json_array := JSON_ARRAY_T.parse(p_json_articole);
            
            FOR i IN 0 .. v_json_array.get_size() - 1 LOOP
                v_articol_obj := TREAT(v_json_array.get(i) AS JSON_OBJECT_T);
                
                -- Extrage datele articolului
                v_sku := v_articol_obj.get_string('sku');
                v_cantitate_web := v_articol_obj.get_number('cantitate');
                v_pret_web := v_articol_obj.get_number('pret');
                
                log_operation('INFO', 'IMPORTA_COMANDA', p_nr_comanda_ext,
                    'Procesez articol: ' || v_sku || ', cant: ' || v_cantitate_web);
                
                -- STEP 3: Găsește mapările pentru acest SKU
                FOR art_rec IN (
                    SELECT * FROM TABLE(gaseste_articol_roa(v_sku, v_pret_web, v_cantitate_web))
                ) LOOP
                    IF art_rec.success = 1 THEN
                        -- Adaugă articolul la comandă
                        BEGIN
                            PACK_COMENZI.adauga_articol_comanda(
                                p_id_comanda => v_id_comanda,
                                p_id_articol => art_rec.id_articol,
                                p_cantitate => art_rec.cantitate_roa,
                                p_pret => art_rec.pret_unitar,
                                p_id_pol => c_id_pol,
                                p_id_util => c_id_util
                            );
                            
                            v_articole_procesate := v_articole_procesate + 1;
                            
                            log_operation('INFO', 'IMPORTA_COMANDA', p_nr_comanda_ext,
                                'Articol adăugat: ' || art_rec.codmat || 
                                ', cant: ' || art_rec.cantitate_roa ||
                                ', preț: ' || art_rec.pret_unitar);
                                
                        EXCEPTION
                            WHEN OTHERS THEN
                                v_articole_eroare := v_articole_eroare + 1;
                                log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
                                    'Eroare la adăugare articol ' || art_rec.codmat, SQLERRM);
                        END;
                    ELSE
                        v_articole_eroare := v_articole_eroare + 1;
                        log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
                            'SKU nu a putut fi mapat: ' || v_sku || ' - ' || art_rec.error_message);
                    END IF;
                END LOOP;
            END LOOP;
            
        EXCEPTION
            WHEN OTHERS THEN
                log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
                    'Eroare la procesarea JSON articole', SQLERRM);
                RETURN -1;
        END;
        
        -- Verifică dacă s-au procesat articole cu succes
        IF v_articole_procesate = 0 THEN
            log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
                'Niciun articol nu a fost procesat cu succes');
            RETURN -1;
        END IF;
        
        -- Log sumar final
        log_operation('INFO', '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');
        
        RETURN v_id_comanda;
        
    EXCEPTION
        WHEN OTHERS THEN
            log_operation('ERROR', 'IMPORTA_COMANDA', p_nr_comanda_ext,
                'Eroare neașteptată în importa_comanda_web', SQLERRM);
            RETURN -1;
    END importa_comanda_web;

END IMPORT_COMENZI;
/

-- ====================================================================
-- Tabel pentru logging (opțional - poate fi înlocuit cu logging extern)
-- ====================================================================
CREATE TABLE import_log (
    id_log          NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    log_time        DATE DEFAULT SYSDATE,
    log_level       VARCHAR2(10),     -- INFO, WARN, ERROR
    operation       VARCHAR2(50),     -- GASESTE_ARTICOL, IMPORTA_COMANDA
    reference_id    VARCHAR2(100),    -- SKU sau Nr_Comanda
    message         VARCHAR2(4000),
    details         CLOB
);

-- Index pentru căutări rapide în log
CREATE INDEX idx_import_log_time ON import_log(log_time);
CREATE INDEX idx_import_log_ref ON import_log(reference_id, operation);

-- Comentarii pentru documentație
COMMENT ON TABLE import_log IS 'Log pentru operațiile de import comenzi web';
COMMENT ON COLUMN import_log.operation IS 'Tipul operației: GASESTE_ARTICOL, IMPORTA_COMANDA, VALIDEAZA_SET';
COMMENT ON COLUMN import_log.reference_id IS 'Referința: SKU pentru articole, Nr_Comanda pentru comenzi';

-- Grant-uri pentru utilizarea package-ului
-- GRANT EXECUTE ON IMPORT_COMENZI TO PUBLIC;
-- GRANT SELECT, INSERT ON import_log TO PUBLIC;

-- ====================================================================
-- Exemple de utilizare și testare
-- ====================================================================

/*
-- Exemplu 1: Căutare articol simplu (direct în nomenclator)
SELECT * FROM TABLE(IMPORT_COMENZI.gaseste_articol_roa('CAF01', 15.50, 2));

-- Exemplu 2: Căutare articol cu reîmpachetare
SELECT * FROM TABLE(IMPORT_COMENZI.gaseste_articol_roa('CAFE100', 150.00, 1));

-- Exemplu 3: Căutare set compus
SELECT * FROM TABLE(IMPORT_COMENZI.gaseste_articol_roa('SET01', 200.00, 1));

-- Exemplu 4: Import comandă completă
DECLARE
    v_json_articole CLOB := '[
        {"sku": "CAF01", "cantitate": 2, "pret": 15.50},
        {"sku": "CAFE100", "cantitate": 1, "pret": 150.00},
        {"sku": "SET01", "cantitate": 1, "pret": 200.00}
    ]';
    v_result NUMBER;
BEGIN
    v_result := IMPORT_COMENZI.importa_comanda_web(
        p_nr_comanda_ext => 'WEB-TEST-001',
        p_data_comanda => SYSDATE,
        p_id_partener => 12345,  -- ID partener valid din sistem
        p_json_articole => v_json_articole,
        p_observatii => 'Test import din sistem web'
    );
    
    DBMS_OUTPUT.PUT_LINE('ID Comandă creată: ' || v_result);
END;
/

-- Interogare log pentru troubleshooting
SELECT log_time, log_level, operation, reference_id, message 
FROM import_log 
WHERE log_time >= SYSDATE - 1  -- Ultimele 24h
ORDER BY log_time DESC;
*/

-- ====================================================================
-- Finalizare
-- ====================================================================