/**
 * Tests for Django Server React Context
 *
 * Unit tests run without backend.
 * Integration tests require: docker-compose up
 *
 * Run integration tests with: RUN_INTEGRATION_TESTS=true npm run test
 */

import React from 'react'
import { render, screen, waitFor, act } from '@testing-library/react'
import {
    MizanProvider,
    useMizan,
    useMizanStatus,
    useMizanCall,
    // Legacy aliases for backwards compatibility tests
    MizanProvider,
    useDjango,
    useMizanStatus,
    useMizanCall,
} from '../context'
import { MizanError } from '../errors'
import { describeIntegration, BACKEND_URL } from '../testing'

// ============================================================================
// Unit Tests (no backend required)
// ============================================================================

describe('mizan Context (unit)', () => {
    describe('useMizan hook', () => {
        it('should throw when used outside provider', () => {
            function TestComponent() {
                useMizan()
                return <div>Test</div>
            }

            const consoleSpy = jest.spyOn(console, 'error').mockImplementation()

            expect(() => render(<TestComponent />)).toThrow(
                'useMizan must be used within a MizanProvider'
            )

            consoleSpy.mockRestore()
        })

        it('should return context value inside provider', () => {
            let contextValue: any = null

            function TestComponent() {
                contextValue = useMizan()
                return <div>Test</div>
            }

            render(
                <MizanProvider autoConnect={false}>
                    <TestComponent />
                </MizanProvider>
            )

            expect(contextValue).not.toBeNull()
            expect(contextValue!.status).toBe('disconnected')
        })
    })

    describe('useMizanStatus hook', () => {
        it('should return disconnected when autoConnect is false', () => {
            function TestComponent() {
                const status = useMizanStatus()
                return <div data-testid="status">{status}</div>
            }

            render(
                <MizanProvider autoConnect={false}>
                    <TestComponent />
                </MizanProvider>
            )

            expect(screen.getByTestId('status')).toHaveTextContent('disconnected')
        })
    })

    describe('hydration', () => {
        it('should initialize context store from hydration data', () => {
            let contextValue: any = null

            function TestComponent() {
                contextValue = useMizan()
                return <div>Test</div>
            }

            const hydration = {
                auth_status: { is_authenticated: false },
                user: null,
            }

            render(
                <MizanProvider hydration={hydration} autoConnect={false}>
                    <TestComponent />
                </MizanProvider>
            )

            expect(contextValue.getContext('auth_status')).toEqual({ is_authenticated: false })
            expect(contextValue.getContext('user')).toEqual(null)
        })
    })
})

// ============================================================================
// Integration Tests (require running backend)
// ============================================================================

describeIntegration('mizan Context (integration)', () => {
    describe('server function calls via HTTP', () => {
        it('should call echo function and get response', async () => {
            let result: any = null
            let error: any = null

            function TestComponent() {
                const { call, status } = useMizan()

                React.useEffect(() => {
                    // Use HTTP fallback (status will be disconnected without WebSocket)
                    call<{ text: string }, { message: string }>('echo', { text: 'context test' })
                        .then((r) => { result = r })
                        .catch((e) => { error = e })
                }, [call])

                return <div data-testid="status">{status}</div>
            }

            render(
                <MizanProvider baseUrl={`${BACKEND_URL}/api/mizan`} autoConnect={false}>
                    <TestComponent />
                </MizanProvider>
            )

            await waitFor(() => {
                expect(result || error).not.toBeNull()
            }, { timeout: 5000 })

            expect(error).toBeNull()
            expect(result).toHaveProperty('message')
            expect(result.message).toContain('context test')
        })

        it('should call add function with correct result', async () => {
            let result: any = null
            let error: any = null

            function TestComponent() {
                const { call } = useMizan()

                React.useEffect(() => {
                    call<{ a: number; b: number }, { result: number }>('add', { a: 10, b: 20 })
                        .then((r) => { result = r })
                        .catch((e) => { error = e })
                }, [call])

                return <div>Test</div>
            }

            render(
                <MizanProvider baseUrl={`${BACKEND_URL}/api/mizan`} autoConnect={false}>
                    <TestComponent />
                </MizanProvider>
            )

            await waitFor(() => {
                expect(result || error).not.toBeNull()
            }, { timeout: 5000 })

            expect(error).toBeNull()
            expect(result).toEqual({ result: 30 })
        })

        it('should throw MizanError for validation errors', async () => {
            let result: any = null
            let error: any = null

            function TestComponent() {
                const { call } = useMizan()

                React.useEffect(() => {
                    // Call without required field
                    call('echo', {})
                        .then((r) => { result = r })
                        .catch((e) => { error = e })
                }, [call])

                return <div>Test</div>
            }

            render(
                <MizanProvider baseUrl={`${BACKEND_URL}/api/mizan`} autoConnect={false}>
                    <TestComponent />
                </MizanProvider>
            )

            await waitFor(() => {
                expect(result || error).not.toBeNull()
            }, { timeout: 5000 })

            expect(result).toBeNull()
            expect(error).toBeInstanceOf(MizanError)
        })
    })

    describe('useMizanCall hook', () => {
        it('should create typed function that calls backend', async () => {
            let result: any = null
            let error: any = null

            interface EchoInput { text: string }
            interface EchoOutput { message: string }

            function TestComponent() {
                const echo = useMizanCall<EchoInput, EchoOutput>('echo')

                React.useEffect(() => {
                    echo({ text: 'typed function test' })
                        .then((r) => { result = r })
                        .catch((e) => { error = e })
                }, [echo])

                return <div>Test</div>
            }

            render(
                <MizanProvider baseUrl={`${BACKEND_URL}/api/mizan`} autoConnect={false}>
                    <TestComponent />
                </MizanProvider>
            )

            await waitFor(() => {
                expect(result || error).not.toBeNull()
            }, { timeout: 5000 })

            expect(error).toBeNull()
            expect(result).toHaveProperty('message')
            expect(result.message).toContain('typed function test')
        })
    })

    describe('form functions', () => {
        it('should call login.schema and get form fields', async () => {
            let result: any = null
            let error: any = null

            function TestComponent() {
                const { call } = useMizan()

                React.useEffect(() => {
                    call('login.schema', { data: {} })
                        .then((r) => { result = r })
                        .catch((e) => { error = e })
                }, [call])

                return <div>Test</div>
            }

            render(
                <MizanProvider baseUrl={`${BACKEND_URL}/api/mizan`} autoConnect={false}>
                    <TestComponent />
                </MizanProvider>
            )

            await waitFor(() => {
                expect(result || error).not.toBeNull()
            }, { timeout: 5000 })

            expect(error).toBeNull()
            expect(result).toHaveProperty('fields')
            expect(result).toHaveProperty('meta')
            // Login form should have login and password fields
            expect(result.fields).toHaveProperty('login')
            expect(result.fields).toHaveProperty('password')
        })

        it('should call login.validate and get validation result', async () => {
            let result: any = null
            let error: any = null

            function TestComponent() {
                const { call } = useMizan()

                React.useEffect(() => {
                    call('login.validate', {
                        data: { login: 'test@example.com', password: 'testpass' }
                    })
                        .then((r) => { result = r })
                        .catch((e) => { error = e })
                }, [call])

                return <div>Test</div>
            }

            render(
                <MizanProvider baseUrl={`${BACKEND_URL}/api/mizan`} autoConnect={false}>
                    <TestComponent />
                </MizanProvider>
            )

            await waitFor(() => {
                expect(result || error).not.toBeNull()
            }, { timeout: 5000 })

            // Should return validation result (may have errors for invalid creds, that's ok)
            expect(error).toBeNull()
            expect(result).toHaveProperty('valid')
        })
    })

})
