import { createClient } from '@supabase/supabase-js';
import * as dotenv from 'dotenv';
import { XMLParser } from 'fast-xml-parser';
import * as cron from 'node-cron';
import { KsefClient } from 'ksef-client';

dotenv.config();

const SUPABASE_URL = process.env.SUPABASE_URL || '';
const SUPABASE_SERVICE_KEY = process.env.SUPABASE_SERVICE_KEY || '';

if (!SUPABASE_URL || !SUPABASE_SERVICE_KEY) {
    console.error('Brak zmiennych środowiskowych SUPABASE_URL lub SUPABASE_SERVICE_KEY');
    process.exit(1);
}

const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_KEY);
const parser = new XMLParser({
    ignoreAttributes: false,
    attributeNamePrefix: '@_',
});

/**
 * Pobiera metadane faktur zgodnie z rekomendacją MF (PermanentStorage / Invoicing)
 */
async function KsefMetadataQuery(client: KsefClient, fromDate: string) {
    const toDate = new Date().toISOString();
    console.log(`[QUERY] Zakres KSeF: ${fromDate} do: ${toDate}`);

    const subjects = ["Subject1", "Subject2"];
    let allInvoices: any[] = [];

    for (const subject of subjects) {
        try {
            const dateType = subject === "Subject2" ? "PermanentStorage" : "Invoicing";
            console.log(`   -> Odpytywanie ${subject} (${dateType})...`);

            const response: any = await client.invoices.queryInvoiceMetadata(
                {
                    subjectType: subject as any,
                    dateRange: {
                        dateType: dateType as any,
                        from: fromDate as any,
                        to: toDate as any
                    },
                },
                0,
                100,
                "Desc"
            );

            if (response.invoices) {
                const list = response.invoices as any[];
                console.log(`      Znaleziono ${list.length} faktur.`);
                allInvoices = [...allInvoices, ...list];
            }
        } catch (e: any) {
            console.error(`   [!] Błąd ${subject}:`, e.message);
        }
    }

    return Array.from(new Map(allInvoices.map(item => [item.ksefNumber, item])).values());
}

async function fetchInvoiceXml(client: KsefClient, ksefRef: string) {
    return await client.invoices.getInvoice(ksefRef);
}

async function syncAllCompanies() {
    const VERSION = "1.3.1 (3-Month Limit)";
    console.log(`--- Start cyklu synchronizacji [v${VERSION}] ---`);

    const { data: sessions, error } = await supabase
        .from('ksef_sessions')
        .select('*');

    if (error || !sessions) {
        console.error('Błąd pobierania sesji:', error);
        return;
    }

    for (const session of sessions) {
        const { user_id, nip, auth_token, environment, last_sync, is_sync_requested, is_full_sync_requested } = session;

        const ksefEnv = (environment === 'PRD' || environment === 'prod') ? 'PRD' : 'TEST';
        console.log(`NIP: ${nip} | Envir: ${ksefEnv}`);

        await supabase.from('ksef_sessions').update({ sync_status: 'syncing', last_sync_error: null }).eq('user_id', user_id);

        const client = new KsefClient({ environment: ksefEnv as any });

        try {
            const tokens = await client.workflows.auth.authenticateWithKsefToken({
                token: auth_token,
                context: { type: 'Nip', value: nip },
                pollIntervalMs: 2000,
                maxAttempts: 30,
            });
            client.authManager.setTokens(tokens);

            const defaultSyncStart = "2026-01-01T00:00:00Z";
            let syncFromValue = last_sync || defaultSyncStart;

            // Logika zakresu czasu (v1.4.1):
            if (is_full_sync_requested) {
                // Pełna historia: MAX 85 dni (dla bezpieczeństwa przed limitem 3-miesięcznym KSeF)
                const eightyFiveDaysAgo = new Date(Date.now() - 85 * 24 * 60 * 60 * 1000).toISOString();
                syncFromValue = eightyFiveDaysAgo;
                console.log(`[MODE: FULL HISTORY] Skan 85 dni: ${syncFromValue}`);
            } else if (is_sync_requested) {
                // Ręczny: 7 dni wstecz dla pewności (leczenie "dziur")
                syncFromValue = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
                console.log(`[MODE: MANUAL] Szeroki skan (7 dni): ${syncFromValue}`);
            } else {
                // Auto: 1 godzina zapasu od ostatniego sync
                syncFromValue = new Date(new Date(syncFromValue).getTime() - 60 * 60000).toISOString();
                console.log(`[MODE: AUTO] Mały zapas (1h): ${syncFromValue}`);
            }

            const headers: any[] = await KsefMetadataQuery(client, syncFromValue);
            console.log(`Łącznie znaleziono: ${headers.length} nagłówków do sprawdzenia.`);

            for (const meta of headers) {
                const ksefRef = meta.ksefNumber;

                const { data: existing } = await supabase
                    .from('invoices')
                    .select('id')
                    .eq('ksef_reference_number', ksefRef)
                    .single();

                if (existing) continue;

                try {
                    const xmlRaw = await fetchInvoiceXml(client, ksefRef);
                    const parsed = parser.parse(xmlRaw);

                    const fa = parsed?.Faktura?.Fa;
                    const podmiot = (subject: string) => parsed?.Faktura?.[subject]?.DaneIdentyfikacyjne;

                    if (fa) {
                        const newInvoice = {
                            ksef_reference_number: ksefRef,
                            acquisition_timestamp: meta.acquisitionDate || meta.acquisitionTimestamp,
                            invoice_number: meta.invoiceNumber || fa.P_2,
                            seller_name: meta.seller?.name || podmiot('Podmiot1')?.Nazwa || 'Brak danych',
                            seller_nip: meta.seller?.nip || podmiot('Podmiot1')?.NIP || 'Brak danych',
                            date_issue: meta.issueDate || fa.P_1,
                            date_sales: fa.P_6 || meta.issueDate || fa.P_1,
                            net_amount: meta.netAmount !== undefined ? meta.netAmount : (fa.P_13_1 ? parseFloat(fa.P_13_1) : null),
                            gross_amount: meta.grossAmount !== undefined ? meta.grossAmount : (fa.P_15 ? parseFloat(fa.P_15) : null),
                            currency_code: meta.currency || fa.kodWaluty || 'PLN',
                            xml_content: xmlRaw,
                            owner_id: user_id
                        };

                        await supabase.from('invoices').insert(newInvoice);
                        console.log(`   [POBRANO] ${newInvoice.invoice_number}`);
                    }
                } catch (e: any) {
                    console.error(`   Błąd procesowania ${ksefRef}:`, e.message);
                }

                await new Promise(r => setTimeout(r, 1000));
            }

            // Sukces: Reset flag i statusu
            await supabase.from('ksef_sessions')
                .update({
                    last_sync: new Date().toISOString(),
                    is_sync_requested: false,
                    is_full_sync_requested: false,
                    sync_status: 'idle'
                })
                .eq('user_id', user_id);

        } catch (e: any) {
            console.error(`Błąd sesji NIP ${nip}:`, e.message || e);
            await supabase.from('ksef_sessions')
                .update({
                    sync_status: 'error',
                    last_sync_error: (e.message?.includes('429') || e.response?.status === 429)
                        ? "Limit zapytan KSeF (Rate Limit). Prosze odczekac kilka minut."
                        : (e.message || 'Nieznany bląd'),
                    is_sync_requested: false,
                    is_full_sync_requested: false
                })
                .eq('user_id', user_id);
        } finally {
            try {
                client.authManager.setTokens({ accessToken: { token: '' }, referenceNumber: '' } as any);
            } catch (e) { }
        }
    }

    console.log('--- Cykl zakończony ---');
}

// Polling dla ręcznych wywołań co 30 sekund
setInterval(async () => {
    const { data } = await supabase
        .from('ksef_sessions')
        .select('user_id')
        .or('is_sync_requested.eq.true,is_full_sync_requested.eq.true');

    if (data && data.length > 0) {
        console.log('[TRIGGER] Wykryto żądanie synchronizacji (Manual/Full).');
        syncAllCompanies();
    }
}, 30000);

// Harmonogram co 15 minut (MF)
cron.schedule('*/15 * * * *', () => {
    syncAllCompanies();
});

// Start przy uruchomieniu
syncAllCompanies();
