/**
 * @jest-environment node
 */
import { DrizzleDatabase } from '../drizzle'
import { DatabaseConfig } from '../types'
import Database from 'better-sqlite3'
import { drizzle } from 'drizzle-orm/better-sqlite3'
import * as schema from '@fitai/database'

describe('DrizzleDatabase', () => {
    let db: DrizzleDatabase
    let sqlite: Database.Database
    let drizzleDb: ReturnType<typeof drizzle>

    beforeEach(async () => {
        // Create in-memory SQLite database
        sqlite = new Database(':memory:')
        drizzleDb = drizzle(sqlite, { schema })

        const config: DatabaseConfig = {
            type: 'sqlite',
            connection: { filename: ':memory:' },
            options: { logging: false }
        }

        // Initialize DrizzleDatabase with the in-memory db
        db = new DrizzleDatabase(config, drizzleDb as any)
        await db.connect()
    })

    afterEach(async () => {
        await db.disconnect()
        sqlite.close()
    })

    describe('User Operations', () => {
        it('should create and retrieve a user', async () => {
            const newUser = {
                email: 'test@example.com',
                firstName: 'Test',
                lastName: 'User',
                password: 'password123',
                role: 'client' as const
            }

            const createdUser = await db.createUser(newUser)
            expect(createdUser).toBeDefined()
            expect(createdUser.id).toBeDefined()
            expect(createdUser.email).toBe(newUser.email)

            const fetchedUser = await db.getUserById(createdUser.id)
            expect(fetchedUser).toBeDefined()
            expect(fetchedUser?.id).toBe(createdUser.id)
            expect(fetchedUser?.email).toBe(createdUser.email)
            // SQLite/Drizzle might truncate ms or handle dates differently in test env
            expect(new Date(fetchedUser!.createdAt).getTime()).toBeLessThanOrEqual(createdUser.createdAt.getTime())
            expect(new Date(fetchedUser!.updatedAt).getTime()).toBeLessThanOrEqual(createdUser.updatedAt.getTime())
        })

        it('should update a user', async () => {
            const user = await db.createUser({
                email: 'update@example.com',
                firstName: 'Update',
                lastName: 'Me',
                password: 'password',
                role: 'client'
            })

            const updated = await db.updateUser(user.id, { firstName: 'Updated' })
            expect(updated?.firstName).toBe('Updated')

            const fetched = await db.getUserById(user.id)
            expect(fetched?.firstName).toBe('Updated')
        })

        it('should delete a user', async () => {
            const user = await db.createUser({
                email: 'delete@example.com',
                firstName: 'Delete',
                lastName: 'Me',
                password: 'password',
                role: 'client'
            })

            const deleted = await db.deleteUser(user.id)
            expect(deleted).toBe(true)

            const fetched = await db.getUserById(user.id)
            expect(fetched).toBeNull()
        })
    })

    describe('Client Operations', () => {
        it('should create and retrieve a client', async () => {
            const user = await db.createUser({
                email: 'client@example.com',
                firstName: 'Client',
                lastName: 'User',
                password: 'password',
                role: 'client'
            })

            const newClient = {
                userId: user.id,
                membershipType: 'basic' as const,
                membershipStatus: 'active' as const,
                joinDate: new Date()
            }

            const createdClient = await db.createClient(newClient)
            expect(createdClient).toBeDefined()
            expect(createdClient.userId).toBe(user.id)

            const fetchedClient = await db.getClientById(createdClient.id)
            expect(fetchedClient).toBeDefined()
            expect(fetchedClient?.id).toBe(createdClient.id)
            expect(fetchedClient?.userId).toBe(createdClient.userId)
            expect(fetchedClient?.membershipType).toBe(createdClient.membershipType)
        })
    })

    describe('Fitness Profile Operations', () => {
        it('should create and retrieve a fitness profile', async () => {
            const user = await db.createUser({
                email: 'profile@example.com',
                firstName: 'Profile',
                lastName: 'User',
                password: 'password',
                role: 'client'
            })

            const newProfile = {
                userId: user.id,
                height: 180,
                weight: 75,
                age: 30,
                gender: 'male' as const,
                activityLevel: 'moderately_active' as const,
                fitnessGoals: ['weight_loss'],
                exerciseHabits: 'None',
                dietHabits: 'None',
                medicalConditions: 'None'
            }

            const createdProfile = await db.createFitnessProfile(newProfile as any)
            expect(createdProfile).toBeDefined()
            expect(createdProfile.userId).toBe(user.id)

            const fetchedProfile = await db.getFitnessProfileByUserId(user.id)
            expect(fetchedProfile).toBeDefined()
            expect(fetchedProfile?.userId).toBe(user.id)
            expect(fetchedProfile?.fitnessGoals).toEqual(expect.arrayContaining(['weight_loss']))
        })
    })

    describe('Attendance Operations', () => {
        it('should check in and check out', async () => {
            const user = await db.createUser({
                email: 'attendance@example.com',
                firstName: 'Attendance',
                lastName: 'User',
                password: 'password',
                role: 'client'
            })

            const checkIn = await db.checkIn(user.id, 'gym')
            expect(checkIn).toBeDefined()
            expect(checkIn.checkOutTime).toBeUndefined()

            const activeCheckIn = await db.getActiveCheckIn(user.id)
            expect(activeCheckIn).toBeDefined()
            expect(activeCheckIn?.id).toBe(checkIn.id)

            const checkOut = await db.checkOut(checkIn.id)
            expect(checkOut?.checkOutTime).toBeDefined()

            const activeCheckInAfter = await db.getActiveCheckIn(user.id)
            expect(activeCheckInAfter).toBeNull()
        })
    })
})
