/**
 * Tests for Django Forms
 *
 * Integration tests call the REAL backend - no mocks.
 * Backend must be running: docker-compose up
 *
 * Run integration tests with: RUN_INTEGRATION_TESTS=true npm run test
 */

import React from 'react'
import { renderHook, act, waitFor } from '@testing-library/react'
import { z } from 'zod'

import {
    useDjangoFormCore,
    type FormCoreConfig,
} from '../forms'
import { DjangoContext } from '../context'
import { describeIntegration, BACKEND_URL } from '../testing'

// ============================================================================
// Test Setup
// ============================================================================

// Helper to render hook with provider
function renderFormHook<TData extends Record<string, unknown>>(
    config: FormCoreConfig<TData>
) {
    return renderHook(() => useDjangoFormCore<TData>(config), {
        wrapper: ({ children }) => (
            <DjangoContext baseUrl={`${BACKEND_URL}/api/mizan`} autoConnect={false}>
                {children}
            </DjangoContext>
        ),
    })
}

// ============================================================================
// Integration Tests - Real Backend Calls
// ============================================================================

describeIntegration('useDjangoFormCore (integration)', () => {
    describe('Schema loading from real backend', () => {
        it('loads login form schema', async () => {
            const { result } = renderFormHook({
                name: 'login',
            })

            expect(result.current.loading).toBe(true)

            await waitFor(() => {
                expect(result.current.loading).toBe(false)
            }, { timeout: 5000 })

            expect(result.current.schema).not.toBeNull()
            expect(result.current.schema?.name).toBe('login')
            // Login form should have login and password fields
            expect(result.current.schema?.fields).toHaveProperty('login')
            expect(result.current.schema?.fields).toHaveProperty('password')
        })

        it('loads signup form schema', async () => {
            const { result } = renderFormHook({
                name: 'signup',
            })

            await waitFor(() => {
                expect(result.current.loading).toBe(false)
            }, { timeout: 5000 })

            expect(result.current.schema).not.toBeNull()
            expect(result.current.schema?.name).toBe('signup')
            // Signup form should have email and password fields
            expect(result.current.schema?.fields).toHaveProperty('email')
            expect(result.current.schema?.fields).toHaveProperty('password1')
        })

        it('loads add_email form schema', async () => {
            const { result } = renderFormHook({
                name: 'add_email',
            })

            await waitFor(() => {
                expect(result.current.loading).toBe(false)
            }, { timeout: 5000 })

            expect(result.current.schema).not.toBeNull()
            expect(result.current.schema?.fields).toHaveProperty('email')
        })
    })

    describe('Form data management', () => {
        it('sets and gets form data', async () => {
            const { result } = renderFormHook({
                name: 'login',
            })

            await waitFor(() => {
                expect(result.current.loading).toBe(false)
            }, { timeout: 5000 })

            act(() => {
                result.current.set('login', 'test@example.com')
                result.current.set('password', 'testpassword123')
            })

            expect(result.current.data.login).toBe('test@example.com')
            expect(result.current.data.password).toBe('testpassword123')
        })

        it('tracks touched fields', async () => {
            const { result } = renderFormHook({
                name: 'login',
            })

            await waitFor(() => {
                expect(result.current.loading).toBe(false)
            }, { timeout: 5000 })

            expect(result.current.touchedFields.size).toBe(0)

            act(() => {
                result.current.touch('login')
            })

            expect(result.current.touchedFields.has('login')).toBe(true)
            expect(result.current.touchedFields.has('password')).toBe(false)
        })

        it('resets form state', async () => {
            const { result } = renderFormHook({
                name: 'login',
            })

            await waitFor(() => {
                expect(result.current.loading).toBe(false)
            }, { timeout: 5000 })

            act(() => {
                result.current.set('login', 'changed@example.com')
                result.current.touch('login')
            })

            expect(result.current.data.login).toBe('changed@example.com')
            expect(result.current.touchedFields.has('login')).toBe(true)

            act(() => {
                result.current.reset()
            })

            expect(result.current.data.login).toBe('')
            expect(result.current.touchedFields.size).toBe(0)
            expect(result.current.errors).toBeNull()
        })
    })

    describe('Zod validation with real schema', () => {
        // Define Zod schema matching login form
        const LoginZodSchema = z.object({
            login: z.string().min(1, 'Login is required').email('Invalid email'),
            password: z.string().min(1, 'Password is required'),
        })

        type LoginData = z.infer<typeof LoginZodSchema>

        it('validates with Zod schema on touch', async () => {
            const { result } = renderFormHook<LoginData>({
                name: 'login',
                zodSchema: LoginZodSchema,
            })

            await waitFor(() => {
                expect(result.current.loading).toBe(false)
            }, { timeout: 5000 })

            // Set invalid value
            act(() => {
                result.current.set('login', 'not-an-email')
            })

            // Touch triggers validation
            act(() => {
                result.current.touch('login')
            })

            // Zod validation should show email format error
            expect(result.current.errors?.fields.login).toBeDefined()
            expect(result.current.errors?.fields.login?.[0]?.message).toBe('Invalid email')
            expect(result.current.errors?.fields.login?.[0]?.source).toBe('zod')
        })

        it('clears errors when value becomes valid', async () => {
            const { result } = renderFormHook<LoginData>({
                name: 'login',
                zodSchema: LoginZodSchema,
            })

            await waitFor(() => {
                expect(result.current.loading).toBe(false)
            }, { timeout: 5000 })

            // Set invalid value and touch
            act(() => {
                result.current.set('login', 'bad')
            })
            act(() => {
                result.current.touch('login')
            })

            expect(result.current.errors?.fields.login).toBeDefined()

            // Set valid value and touch
            act(() => {
                result.current.set('login', 'valid@example.com')
            })
            act(() => {
                result.current.touch('login')
            })

            expect(result.current.errors?.fields.login).toBeUndefined()
        })

        it('tracks hasErrors correctly', async () => {
            const { result } = renderFormHook<LoginData>({
                name: 'login',
                zodSchema: LoginZodSchema,
            })

            await waitFor(() => {
                expect(result.current.loading).toBe(false)
            }, { timeout: 5000 })

            expect(result.current.hasErrors).toBe(false)

            // Set invalid and touch
            act(() => {
                result.current.set('login', 'bad')
            })
            act(() => {
                result.current.touch('login')
            })

            expect(result.current.hasErrors).toBe(true)

            // Set valid and touch
            act(() => {
                result.current.set('login', 'good@example.com')
            })
            act(() => {
                result.current.touch('login')
            })

            expect(result.current.hasErrors).toBe(false)
        })
    })

    describe('Form submission', () => {
        it('submits login form and handles validation errors', async () => {
            const { result } = renderFormHook({
                name: 'login',
            })

            await waitFor(() => {
                expect(result.current.loading).toBe(false)
            }, { timeout: 5000 })

            // Set invalid credentials
            act(() => {
                result.current.set('login', 'nonexistent@example.com')
                result.current.set('password', 'wrongpassword')
            })

            // Submit should fail with validation error
            let submitResult: any
            await act(async () => {
                submitResult = await result.current.submit()
            })

            // Submit should return error (invalid credentials)
            // The exact error depends on backend behavior
            expect(submitResult).toBeDefined()
        })

        it('submits signup form with missing required fields', async () => {
            const { result } = renderFormHook({
                name: 'signup',
            })

            await waitFor(() => {
                expect(result.current.loading).toBe(false)
            }, { timeout: 5000 })

            // Submit with empty fields should return validation errors
            let submitResult: any
            await act(async () => {
                submitResult = await result.current.submit()
            })

            // Should have validation errors for required fields
            expect(submitResult).toBeDefined()
            expect(submitResult.success).toBe(false)
        })
    })

    describe('Error source tagging', () => {
        const LoginZodSchema = z.object({
            login: z.string().min(1, 'Login is required').email('Invalid email'),
            password: z.string().min(1, 'Password is required'),
        })

        type LoginData = z.infer<typeof LoginZodSchema>

        it('tags Zod errors with source: zod', async () => {
            const { result } = renderFormHook<LoginData>({
                name: 'login',
                zodSchema: LoginZodSchema,
            })

            await waitFor(() => {
                expect(result.current.loading).toBe(false)
            }, { timeout: 5000 })

            act(() => {
                result.current.set('login', 'invalid')
            })
            act(() => {
                result.current.touch('login')
            })

            const errors = result.current.getFieldErrors('login')
            expect(errors.length).toBeGreaterThan(0)
            expect(errors[0].source).toBe('zod')
        })

        it('filters errors by source', async () => {
            const { result } = renderFormHook<LoginData>({
                name: 'login',
                zodSchema: LoginZodSchema,
            })

            await waitFor(() => {
                expect(result.current.loading).toBe(false)
            }, { timeout: 5000 })

            act(() => {
                result.current.set('login', 'invalid')
            })
            act(() => {
                result.current.touch('login')
            })

            // Should have Zod errors
            const zodErrors = result.current.getFieldErrors('login', { source: 'zod' })
            expect(zodErrors.length).toBeGreaterThan(0)

            // Should have no server errors yet
            const serverErrors = result.current.getFieldErrors('login', { source: 'server' })
            expect(serverErrors.length).toBe(0)
        })
    })
})

