import { test, expect, type Page } from '@playwright/test'
import * as fs from 'fs'
import { fileURLToPath } from 'url'
import * as path from 'path'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const SHARED_FILE = path.join(__dirname, '.e2e-shared-exp.json')

function saveShared(data: Record<string, string>) {
    fs.writeFileSync(SHARED_FILE, JSON.stringify(data))
}

function loadShared(): Record<string, string> {
    try {
        return JSON.parse(fs.readFileSync(SHARED_FILE, 'utf-8'))
    } catch {
        return {}
    }
}

const SUFFIX = 'X' + Date.now().toString().slice(-5)  // prefijo X para exportacion
const TODAY = new Date().toISOString().split('T')[0]

const HOMOCLAVE = SUFFIX.slice(-3).toUpperCase()

const PROVEEDOR_RFC = `XAXX010101${HOMOCLAVE}`
const PROVEEDOR_NOMBRE = `Proveedor E2E ${SUFFIX}`

const CLIENTE_RFC = `XBXX010101${HOMOCLAVE}`
const CLIENTE_NOMBRE = `Cliente E2E ${SUFFIX}`

const BROKER_KEY = `T${SUFFIX.slice(-4)}`
const BROKER_LICENSE = SUFFIX.slice(-4).replace(/^0/, '1')
const INVOICE_NUMBER = `E2E-${SUFFIX}`

const PEDIMENTO_YEAR = '26'
const PEDIMENTO_OFFICE = '240'
const PEDIMENTO_LICENSE = '3101'
const PEDIMENTO_NUMBER = SUFFIX.slice(-5)  // 5 digitos para pedimento

async function fillInput(page: Page, selector: string, value: string) {
    await page.locator(selector).click()
    await page.evaluate(({ sel, val }: { sel: string; val: string }) => {
        const el = document.querySelector(sel) as HTMLInputElement
        if (!el) return
        const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set
        setter?.call(el, val)
        el.dispatchEvent(new Event('input', { bubbles: true }))
        el.dispatchEvent(new Event('change', { bubbles: true }))
    }, { sel: selector, val: value })
    await page.waitForTimeout(300)
}

async function fillTextarea(page: Page, selector: string, value: string) {
    await page.locator(selector).click()
    await page.evaluate(({ sel, val }: { sel: string; val: string }) => {
        const el = document.querySelector(sel) as HTMLTextAreaElement
        if (!el) return
        const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value')?.set
        setter?.call(el, val)
        el.dispatchEvent(new Event('input', { bubbles: true }))
        el.dispatchEvent(new Event('change', { bubbles: true }))
    }, { sel: selector, val: value })
    await page.waitForTimeout(500)
}

test.describe('Flujo completo — creacion y actualizacion de factura EXPORTACION', () => {

    test('1. crear proveedor', async ({ page }) => {
        await page.goto('/dashboard/clients_and_providers')
        await page.waitForLoadState('networkidle')

        await page.getByRole('link', { name: /Nuevo/ }).click()
        await expect(page).toHaveURL(/edit/, { timeout: 10000 })
        await page.waitForLoadState('networkidle')

        await fillInput(page, '#rfc', PROVEEDOR_RFC)
        await fillInput(page, '#name', PROVEEDOR_NOMBRE)

        await page.locator('#type').click()
        await page.getByRole('option', { name: 'Proveedor' }).click()

        await page.waitForTimeout(500)

        await page.getByRole('button', { name: /Guardar Socio|Actualizar Socio/ }).click()

        await expect(page).toHaveURL(/clients_and_providers$/, { timeout: 15000 })
        await expect(page.getByText(PROVEEDOR_NOMBRE)).toBeVisible()
    })

    test('2. crear cliente', async ({ page }) => {
        await page.goto('/dashboard/clients_and_providers')
        await page.waitForLoadState('networkidle')

        await page.getByRole('link', { name: /Nuevo/ }).click()
        await expect(page).toHaveURL(/edit/, { timeout: 10000 })
        await page.waitForLoadState('networkidle')

        await fillInput(page, '#rfc', CLIENTE_RFC)
        await fillInput(page, '#name', CLIENTE_NOMBRE)

        await page.locator('#type').click()
        await page.getByRole('option', { name: 'Cliente' }).click()

        await page.waitForTimeout(500)

        await page.getByRole('button', { name: /Guardar Socio|Actualizar Socio/ }).click()

        await expect(page).toHaveURL(/clients_and_providers$/, { timeout: 15000 })
        await expect(page.getByText(CLIENTE_NOMBRE)).toBeVisible()
    })

    test('3. crear agente aduanal', async ({ page }) => {
        await page.goto('/dashboard/customs_brokers')
        await page.waitForLoadState('networkidle')

        await page.getByRole('link', { name: /Nuevo/ }).click()
        await expect(page).toHaveURL(/edit/, { timeout: 20000 })
        await page.waitForLoadState('networkidle')

        await fillInput(page, 'input[placeholder="Ej. 550"]', BROKER_KEY)
        await fillInput(page, 'input[placeholder="Ej. 3421"]', BROKER_LICENSE)
        await fillInput(page, 'input[placeholder="Nombre oficial"]', `Agente E2E ${SUFFIX}`)

        await page.getByRole('button', { name: /Guardar Agente|Actualizar Agente/ }).click()

        await expect(page.getByText(/Agente creado|Agente actualizado/i)).toBeVisible({ timeout: 20000 })
        await expect(page).toHaveURL(/customs_brokers$/, { timeout: 25000 })
    })

    test('4. crear tipo de cambio', async ({ page }) => {
        await page.goto('/dashboard/general_catalogs/exchange-rate')
        await page.waitForLoadState('networkidle')

        // Si ya existe TC del día (creado por auth.setup.ts), no intentar duplicar.
        const todayCell = page.locator('tbody').getByText(new Date().toLocaleDateString('es-MX'))
        if (await todayCell.first().isVisible({ timeout: 2000 }).catch(() => false)) {
            return
        }

        // El botón se llama "Nuevo Registro"; antes era "Nuevo Tipo de Cambio".
        await page.getByRole('button', { name: /Nuevo Registro|Nuevo Tipo de Cambio/ }).click()
        await expect(page.getByRole('heading', { name: 'Nuevo Tipo de Cambio' })).toBeVisible()

        await page.locator('#date').fill(TODAY)
        await page.waitForTimeout(500)

        await fillInput(page, '#value', '17.5')

        await page.getByRole('button', { name: /^Ok$/ }).click()
        await page.getByRole('button', { name: 'Confirmar' }).click()

        await expect(page.getByText(/tipo de cambio creado|tipo de cambio actualizado/i)).toBeVisible({ timeout: 20000 })
    })

    test('5. crear pedimento', async ({ page }) => {
        await page.goto('/dashboard/pedimentos/edit/new')
        await page.waitForLoadState('networkidle')

        await expect(page.getByText('Nuevo Pedimento')).toBeVisible({ timeout: 20000 })

        const prereqModal = page.getByRole('button', { name: /Continuar|Aceptar/ })
        if (await prereqModal.isVisible()) await prereqModal.click()

        await page.waitForTimeout(500)

        // Año — input con id estable
        await fillInput(page, '#year', PEDIMENTO_YEAR)

        // Aduana — bits-ui Select, 1er trigger de la fila superior
        const triggers = page.locator('[data-select-trigger]')
        await triggers.nth(0).click()
        await page.waitForTimeout(800)
        await page.getByRole('option').first().click()
        await page.waitForTimeout(500)

        // Patente — bits-ui Select, 2do trigger
        await triggers.nth(1).click()
        await page.waitForTimeout(800)
        await page.getByRole('option').first().click()
        await page.waitForTimeout(500)

        // Número de pedimento — input con id estable
        await fillInput(page, '#pedimento_number', PEDIMENTO_NUMBER)

        // Clave — bits-ui Select, 3er trigger
        await triggers.nth(2).click()
        await page.waitForTimeout(800)
        await page.getByRole('option').first().click()
        await page.waitForTimeout(500)

        // Tipo de Operación — bits-ui Select, 4to trigger
        await triggers.nth(3).click()
        await page.waitForTimeout(800)
        await page.getByRole('option').first().click()
        await page.waitForTimeout(500)

        // Régimen — bits-ui Select, 5to trigger
        await triggers.nth(4).click()
        await page.waitForTimeout(800)
        await page.getByRole('option').first().click()
        await page.waitForTimeout(800)

        await page.getByRole('button', { name: /Guardar Todos los Cambios/ }).click()

        await expect(page).toHaveURL(/pedimentos$/, { timeout: 20000 })
    })

    test('6. crear factura de exportacion', async ({ page }) => {
        await page.goto('/dashboard/invoices/edit/new?operation_type=exp')
        await page.waitForLoadState('networkidle')

        await expect(page.getByText('Nueva Factura')).toBeVisible({ timeout: 20000 })

        const prereqModal = page.getByRole('button', { name: /Continuar|Aceptar/ })
        if (await prereqModal.isVisible()) await prereqModal.click()

        await page.waitForTimeout(1500)

        await page.locator('#invoice_number').click()
        await page.keyboard.press('Control+A')
        await page.locator('#invoice_number').pressSequentially(INVOICE_NUMBER, { delay: 50 })

        await page.waitForTimeout(1500)

        await page.locator('#invoice_date').fill(TODAY)
        await page.waitForTimeout(500)

        // Helper: click en un botón-selector por su placeholder y elige la 1ra opción.
        const selectFirstByName = async (btnName: RegExp) => {
            await page.getByRole('button', { name: btnName }).first().click()
            await page.waitForTimeout(800)
            await page.getByRole('option').first().click({ force: true })
            await page.waitForTimeout(600)
        }

        // Tipo de factura — sin ID, identificar por placeholder del botón
        await selectFirstByName(/^Tipo de factura$/)

        await page.getByRole('tab', { name: /General/ }).click()
        await page.waitForTimeout(1500)

        // Selectores con IDs estables (siguen existiendo)
        await page.locator('#provider_id').click()
        await page.waitForTimeout(500)
        await page.getByRole('option').first().click()

        await page.locator('#sold_to_id').click()
        await page.waitForTimeout(500)
        await page.getByRole('option').first().click()

        await page.locator('#shipped_to_id').click()
        await page.waitForTimeout(500)
        await page.getByRole('option').first().click()

        // Estos ya no tienen ID — usar el placeholder del botón
        await selectFirstByName(/Agente Aduanal Mex/)
        await selectFirstByName(/Aduana y Secci[oó]n de Despacho/)
        await selectFirstByName(/Clave de R[eé]gimen Aduanero/)

        await page.getByRole('button', { name: /Guardar Todos los Cambios/ }).click()
        await expect(page.getByText('Todos los cambios se guardaron correctamente')).toBeVisible({ timeout: 15000 })
        // Guardar numero de factura para tests posteriores
        saveShared({ INVOICE_NUMBER })
    })

    test('7. factura aparece en la lista de exportacion', async ({ page }) => {
        const shared = loadShared()
        const invoiceNumber = shared.INVOICE_NUMBER || INVOICE_NUMBER

        await page.goto('/dashboard/invoices?operation_type=exp')
        await page.waitForLoadState('networkidle')

        await expect(page.locator('main')).toBeVisible()

        await fillInput(page, '#filter-invoice-number, input[placeholder="No. Factura"]', invoiceNumber)
        await page.waitForTimeout(2000)

        await expect(page.locator('tbody').getByText(invoiceNumber).first()).toBeVisible({ timeout: 10000 })
    })

    // TODO(AS-export-partida): El sheet "Nueva Partida" en exportación abre y se cierra
    // automáticamente antes de que el test pueda interactuar. Comportamiento estable en
    // importación. Probable causa: $effect que cierra el sheet cuando la factura de
    // exportación no tiene vinculación a una factura impo previa.
    // Requiere fix en item-sheet-fa.svelte; mientras tanto, 8, 10 y 11 quedan en fixme.
    test.fixme('8. agregar partida a la factura', async ({ page }) => {
        const shared = loadShared()
        const invoiceNumber = shared.INVOICE_NUMBER || INVOICE_NUMBER

        await page.goto('/dashboard/invoices?operation_type=exp')
        await page.waitForLoadState('networkidle')

        await fillInput(page, '#filter-invoice-number, input[placeholder="No. Factura"]', invoiceNumber)
        await expect(page.locator('tbody').getByText(invoiceNumber).first()).toBeVisible({ timeout: 20000 })

        await page.locator('tbody').getByText(invoiceNumber).first().click()
        await page.getByRole('button', { name: /Editar/ }).click()

        await expect(page).toHaveURL(/invoices\/edit\/\d+/, { timeout: 10000 })
        await page.waitForLoadState('networkidle')

        // Ir a pestaña Partidas. La página de export necesita tiempo extra para
        // hidratar todas las relaciones (factura, items existentes) antes de Agregar.
        await page.getByRole('tab', { name: /Partidas/ }).click()
        await page.waitForLoadState('networkidle', { timeout: 15000 }).catch(() => {})
        await page.waitForTimeout(5000)

        // Abrir sheet de nueva partida. En exportación click+focus a veces no dispara;
        // usar Enter key tras focus. Reintentar si el sheet no aparece.
        const addBtn = page.getByRole('button', { name: /^Agregar Partidas$/ }).first()
        const sheetHeading = page.getByRole('heading', { name: /Nueva Partida/i })
        for (let i = 0; i < 5; i++) {
            await addBtn.scrollIntoViewIfNeeded()
            await addBtn.focus()
            await page.keyboard.press('Enter')
            if (await sheetHeading.isVisible({ timeout: 4000 }).catch(() => false)) break
            await page.waitForTimeout(1500)
        }
        await sheetHeading.waitFor({ state: 'visible', timeout: 15000 })
        await page.waitForTimeout(2000)

        // Helper para seleccionar en dialog y esperar cierre
        async function selectFromDialog(selector: string) {
            await page.locator(selector).click()
            await page.waitForTimeout(800)
            const dialogsBefore = await page.locator('[data-dialog-content]').count()
            await page.locator('[data-dialog-content]').last().locator('tbody tr').first().click()
            await page.waitForFunction(
                (count) => document.querySelectorAll('[data-dialog-content]').length < count,
                dialogsBefore,
                { timeout: 10000 }
            ).catch(() => {})
            await page.waitForTimeout(500)
        }

        // Exportacion requiere vincular a una factura de importacion
        // El bloque "Factura Impo" aparece en el sheet — click en el input readonly
        const sheet = page.locator('[data-slot="sheet-content"]')
        const facturaImpoInput = sheet.locator('input[placeholder="Seleccionar factura..."]').first()
        if (await facturaImpoInput.isVisible({ timeout: 3000 }).catch(() => false)) {
            await facturaImpoInput.click()
            await page.waitForTimeout(500)
            // InvoiceSelectorModal abre con campo de busqueda
            const modalDialog = page.locator('[data-dialog-content]').last()
            if (await modalDialog.isVisible({ timeout: 5000 }).catch(() => false)) {
                // Buscar la factura de importacion por numero para encontrarla
                const searchInput = modalDialog.locator('input[placeholder*="número"], input[placeholder*="numero"], input[type="search"], input[type="text"]').first()
                if (await searchInput.isVisible({ timeout: 2000 }).catch(() => false)) {
                    // Leer numero desde shared file del flujo de importacion
                    const sharedImp = loadShared()  
                    await searchInput.fill(sharedImp.INVOICE_NUMBER || '')
                    // Click en buscar si hay boton
                    const buscarBtn = modalDialog.getByRole('button', { name: /Buscar/i })
                    if (await buscarBtn.isVisible({ timeout: 1000 }).catch(() => false)) {
                        await buscarBtn.click()
                    }
                    await page.waitForTimeout(800)
                }
                // Seleccionar primera fila visible
                const firstRow = modalDialog.locator('tbody tr').first()
                if (await firstRow.isVisible({ timeout: 5000 }).catch(() => false)) {
                    await firstRow.click()
                    await page.waitForTimeout(800)
                } else {
                    // No hay facturas procesadas — cerrar modal y continuar sin vincular
                    await page.keyboard.press('Escape')
                    await page.waitForTimeout(500)
                }
            }
            // Seleccionar linea si el input esta habilitado
            await page.waitForTimeout(500)
            const lineaInput = sheet.locator('#fa_search_line')
            if (await lineaInput.isEnabled({ timeout: 3000 }).catch(() => false)) {
                await lineaInput.click()
                await page.waitForTimeout(800)
                const lineDialog = page.locator('[data-dialog-content]').last()
                if (await lineDialog.isVisible({ timeout: 3000 }).catch(() => false)) {
                    const lineBtn = lineDialog.getByRole('button').first()
                    if (await lineBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
                        await lineBtn.click()
                        await page.waitForTimeout(500)
                    } else {
                        await page.keyboard.press('Escape')
                    }
                }
            }
        }

        await selectFromDialog('#clase')
        await selectFromDialog('#um')
        await selectFromDialog('#pais_origen')

        await page.keyboard.press('Escape')
        await page.waitForTimeout(500)

        // Cantidad
        await fillInput(page, '#cantidad', '10')

        // Costo unitario
        await fillInput(page, '#costo_unitario', '100')

        // Peso neto y bruto — requeridos por el backend
        await fillInput(page, '#peso_neto', '5')
        await fillInput(page, '#peso_bruto', '6')

        // Descripción en español (textarea)
        await fillTextarea(page, '#desc_espanol', `Partida E2E ${SUFFIX}`)

        // Guardar partida — botón "Crear" dentro del sheet
        await sheet.getByRole('button', { name: /^(Crear|Guardar)$/i }).click()
        await page.waitForTimeout(500)

        // Verificar que la partida aparece en la tabla
        await expect(page.locator('tbody').first()).toBeVisible({ timeout: 10000 })

        // Guardar factura completa
        await page.getByRole('button', { name: /Guardar Todos los Cambios/ }).click()
        await expect(page.getByText('Todos los cambios se guardaron correctamente')).toBeVisible({ timeout: 15000 })
    })

    test('9. editar factura existente', async ({ page }) => {
        await page.goto('/dashboard/invoices?operation_type=exp')
        await page.waitForLoadState('networkidle')

        const shared9 = loadShared()
        const invoiceNumber9 = shared9.INVOICE_NUMBER || INVOICE_NUMBER

        await fillInput(page, '#filter-invoice-number, input[placeholder="No. Factura"]', invoiceNumber9)
        await expect(page.locator('tbody').getByText(invoiceNumber9).first()).toBeVisible({ timeout: 20000 })

        await page.locator('tbody').getByText(invoiceNumber9).first().click()
        await page.getByRole('button', { name: /Editar/ }).click()

        await expect(page).toHaveURL(/invoices\/edit\/\d+/, { timeout: 10000 })
        await page.waitForLoadState('networkidle')

        await expect(page.getByText(/Factura #/)).toBeVisible()

        await page.getByRole('tab', { name: /General/ }).click()
        await page.waitForTimeout(1500)

        await page.locator('#customs_broker_id').click()
        await page.waitForTimeout(500)
        await page.getByRole('option').first().click()

        await page.locator('#aduana').click()
        await page.waitForTimeout(500)
        await page.getByRole('option').first().click()

        await page.locator('#document_type').click()
        await page.waitForTimeout(500)
        await page.getByRole('option').first().click()

        await page.getByRole('button', { name: /Guardar Todos los Cambios/ }).click()
        await expect(page.getByText('Todos los cambios se guardaron correctamente')).toBeVisible({ timeout: 15000 })
    })

    test.fixme('10. editar partida existente', async ({ page }) => {
        // Depende de test 8 (sheet inestable en exportación) — ver TODO arriba.
        const shared10 = loadShared()
        const invoiceNumber10 = shared10.INVOICE_NUMBER || INVOICE_NUMBER

        await page.goto('/dashboard/invoices?operation_type=exp')
        await page.waitForLoadState('networkidle')

        await fillInput(page, '#filter-invoice-number, input[placeholder="No. Factura"]', invoiceNumber10)
        await expect(page.locator('tbody').getByText(invoiceNumber10).first()).toBeVisible({ timeout: 20000 })

        await page.locator('tbody').getByText(invoiceNumber10).first().click()
        await page.getByRole('button', { name: /Editar/ }).click()

        await expect(page).toHaveURL(/invoices\/edit\/\d+/, { timeout: 10000 })
        await page.waitForLoadState('networkidle')

        // Ir a pestaña Partidas
        await page.getByRole('tab', { name: /Partidas/ }).click()
        await page.waitForTimeout(500)

        // Abrir sheet de edición — la fila de la partida contiene "E2E01"; el primer botón
        // de la cell de acciones es editar.
        const partidaRow = page.locator('tbody tr').filter({ hasText: 'E2E01' }).first()
        await partidaRow.locator('button').first().click({ force: true, timeout: 10000 })
        await page.waitForTimeout(500)

        // Modificar cantidad
        await fillInput(page, '#cantidad', '20')

        // Modificar costo unitario
        await fillInput(page, '#costo_unitario', '200')

        // Guardar partida editada — botón "Actualizar" dentro del sheet
        const sheetEdit = page.locator('[data-slot="sheet-content"]')
        await sheetEdit.getByRole('button', { name: /^(Actualizar|Guardar)$/i }).click()
        await page.waitForTimeout(500)

        // Guardar factura completa
        await page.getByRole('button', { name: /Guardar Todos los Cambios/ }).click()
        await expect(page.getByText('Todos los cambios se guardaron correctamente')).toBeVisible({ timeout: 15000 })
    })

    test.fixme('11. actualizar factura — verificacion final', async ({ page }) => {
        // Depende de tests 8 y 10 (sheet inestable en exportación) — ver TODO arriba.
        const shared11 = loadShared()
        const invoiceNumber11 = shared11.INVOICE_NUMBER || INVOICE_NUMBER

        await page.goto('/dashboard/invoices?operation_type=exp')
        await page.waitForLoadState('networkidle')

        await fillInput(page, '#filter-invoice-number, input[placeholder="No. Factura"]', invoiceNumber11)
        await expect(page.locator('tbody').getByText(invoiceNumber11).first()).toBeVisible({ timeout: 20000 })

        // Seleccionar la fila
        await page.locator('tbody tr').first().click()
        await page.waitForTimeout(800)

        // Click en boton Actualizar del footer — tiene h-8, gap-1.5 y border
        await page.locator('button.h-8:has([class*="lucide-refresh"])').click()
        await page.waitForTimeout(1500)

        // Verificar que el proceso se ejecuto — puede ser exito o error de validacion de datos
        // El test verifica que el flujo llega hasta el procesamiento, no que los datos sean correctos
        await expect(
            page.getByText(/actualiz|procesad|exito|validaci|error/i).first()
        ).toBeVisible({ timeout: 20000 })
    })

})