import { GROUP_LOG_ACTION } from '@/lib/groups/groupLogActions'
import { GROUP_ROLE_LEADER } from '@/lib/groups/groupMemberRole'
import { GROUP_CREATE_COIN_COST, GROUP_NAME_MAX_LENGTH } from '@/lib/groups/groupSettings'
import type { getDb } from '@/server/db'
import { allocateNextGroupDisplayId } from '@/server/groups/allocateGroupDisplayId'
import { logGroupAction } from '@/server/groups/insertGroupLog'
import { newGroupId } from '@/server/groups/groupIdUtils'

type AppDb = Awaited<ReturnType<typeof getDb>>

export class CreateGroupError extends Error {
    code: string

    constructor(code: string, message: string) {
        super(message)
        this.code = code
        this.name = 'CreateGroupError'
    }
}

export type CreateGroupInput = {
    name: unknown
}

export type CreateGroupResult = {
    groupId: string
    displayId: string
    name: string
    coinBalance: number
}

export async function createGroup(
    db: AppDb,
    creatorUserId: string,
    input: CreateGroupInput
): Promise<CreateGroupResult> {
    const name = String(input.name ?? '').trim()
    if (!name) {
        throw new CreateGroupError('INVALID_NAME', 'Family name is required')
    }
    if (name.length > GROUP_NAME_MAX_LENGTH) {
        throw new CreateGroupError('INVALID_NAME', 'Family name is too long')
    }

    const groupId = newGroupId()
    const now = Math.floor(Date.now() / 1000)

    await db.exec('BEGIN IMMEDIATE')
    try {
        const user = await db.get<{ coin_balance: number }>(
            `SELECT coin_balance FROM users WHERE user_id = ?`,
            [creatorUserId]
        )
        if (!user) {
            throw new CreateGroupError('USER_NOT_FOUND', 'User not found')
        }

        const balance = Number(user.coin_balance ?? 0)
        if (balance < GROUP_CREATE_COIN_COST) {
            throw new CreateGroupError('INSUFFICIENT_COIN_BALANCE', 'Insufficient coin balance')
        }

        const displayId = await allocateNextGroupDisplayId(db)

        const deduct = await db.run(
            `UPDATE users
             SET coin_balance = coin_balance - ?, updated_at = ?
             WHERE user_id = ? AND coin_balance >= ?`,
            [GROUP_CREATE_COIN_COST, now, creatorUserId, GROUP_CREATE_COIN_COST]
        )
        if ((deduct.changes ?? 0) < 1) {
            throw new CreateGroupError('INSUFFICIENT_COIN_BALANCE', 'Insufficient coin balance')
        }

        await db.run(
            `INSERT INTO groups (
                group_id, display_id, name, is_verified, is_can_application, group_level_exp, created_at, updated_at
            ) VALUES (?, ?, ?, 0, 1, 0, ?, ?)`,
            [groupId, displayId, name, now, now]
        )

        await db.run(
            `INSERT INTO user_groups (user_id, group_id, role, join_at) VALUES (?, ?, ?, ?)`,
            [creatorUserId, groupId, GROUP_ROLE_LEADER, now]
        )

        logGroupAction(db, {
            groupId,
            userId: creatorUserId,
            actionContent: GROUP_LOG_ACTION.GROUP_CREATED,
            operationAt: now,
        })

        await db.exec('COMMIT')

        return {
            groupId,
            displayId,
            name,
            coinBalance: balance - GROUP_CREATE_COIN_COST,
        }
    } catch (e) {
        try {
            await db.exec('ROLLBACK')
        } catch {
            // ignore
        }
        throw e
    }
}
