import { moderationCases } from "@db/schema";
import { eq, and, desc } from "drizzle-orm";
import { DrizzleClient } from "@shared/db/DrizzleClient";
import type { CreateCaseOptions, ClearCaseOptions, SearchCasesFilter } from "@/modules/moderation/moderation.types";
import { config } from "@shared/lib/config";
import { getUserWarningEmbed } from "@/modules/moderation/moderation.view";
import { CaseType } from "@shared/lib/constants";

export class ModerationService {
    /**
     * Generate the next sequential case ID
     */
    private static async getNextCaseId(): Promise<string> {
        const latestCase = await DrizzleClient.query.moderationCases.findFirst({
            orderBy: [desc(moderationCases.id)],
        });

        if (!latestCase) {
            return "CASE-0001";
        }

        // Extract number from case ID (e.g., "CASE-0042" -> 42)
        const match = latestCase.caseId.match(/CASE-(\d+)/);
        if (!match || !match[1]) {
            return "CASE-0001";
        }

        const nextNumber = parseInt(match[1], 10) + 1;
        return `CASE-${nextNumber.toString().padStart(4, '0')}`;
    }

    /**
     * Create a new moderation case
     */
    static async createCase(options: CreateCaseOptions) {
        const caseId = await this.getNextCaseId();

        const [newCase] = await DrizzleClient.insert(moderationCases).values({
            caseId,
            type: options.type,
            userId: BigInt(options.userId),
            username: options.username,
            moderatorId: BigInt(options.moderatorId),
            moderatorName: options.moderatorName,
            reason: options.reason,
            metadata: options.metadata || {},
            active: options.type === CaseType.WARN ? true : false, // Only warnings are "active" by default
        }).returning();

        return newCase;
    }

    /**
     * Issue a warning with DM and threshold logic
     */
    static async issueWarning(options: {
        userId: string;
        username: string;
        moderatorId: string;
        moderatorName: string;
        reason: string;
        guildName?: string;
        dmTarget?: { send: (options: any) => Promise<any> };
        timeoutTarget?: { timeout: (duration: number, reason: string) => Promise<any> };
    }) {
        const moderationCase = await this.createCase({
            type: CaseType.WARN,
            userId: options.userId,
            username: options.username,
            moderatorId: options.moderatorId,
            moderatorName: options.moderatorName,
            reason: options.reason,
        });

        if (!moderationCase) {
            throw new Error("Failed to create moderation case");
        }

        const warningCount = await this.getActiveWarningCount(options.userId);

        // Try to DM the user if configured
        if (config.moderation.cases.dmOnWarn && options.dmTarget) {
            try {
                await options.dmTarget.send({
                    embeds: [getUserWarningEmbed(
                        options.guildName || 'this server',
                        options.reason,
                        moderationCase.caseId,
                        warningCount
                    )]
                });
            } catch (error) {
                console.log(`Could not DM warning to ${options.username}: ${error}`);
            }
        }

        // Check for auto-timeout threshold
        let autoTimeoutIssued = false;
        if (config.moderation.cases.autoTimeoutThreshold &&
            warningCount >= config.moderation.cases.autoTimeoutThreshold &&
            options.timeoutTarget) {

            try {
                // Auto-timeout for 24 hours (86400000 ms)
                await options.timeoutTarget.timeout(86400000, `Automatic timeout: ${warningCount} warnings`);

                // Create a timeout case
                await this.createCase({
                    type: CaseType.TIMEOUT,
                    userId: options.userId,
                    username: options.username,
                    moderatorId: "0", // System/Bot
                    moderatorName: "System",
                    reason: `Automatic timeout: reached ${warningCount} warnings`,
                    metadata: { duration: '24h', automatic: true }
                });
                autoTimeoutIssued = true;
            } catch (error) {
                console.error('Failed to auto-timeout user:', error);
            }
        }

        return { moderationCase, warningCount, autoTimeoutIssued };
    }

    /**
     * Get a case by its case ID
     */
    static async getCaseById(caseId: string) {
        return await DrizzleClient.query.moderationCases.findFirst({
            where: eq(moderationCases.caseId, caseId),
        });
    }

    /**
     * Get all cases for a specific user
     */
    static async getUserCases(userId: string, activeOnly: boolean = false) {
        const conditions = [eq(moderationCases.userId, BigInt(userId))];

        if (activeOnly) {
            conditions.push(eq(moderationCases.active, true));
        }

        return await DrizzleClient.query.moderationCases.findMany({
            where: and(...conditions),
            orderBy: [desc(moderationCases.createdAt)],
        });
    }

    /**
     * Get active warnings for a user
     */
    static async getUserWarnings(userId: string) {
        return await DrizzleClient.query.moderationCases.findMany({
            where: and(
                eq(moderationCases.userId, BigInt(userId)),
                eq(moderationCases.type, CaseType.WARN),
                eq(moderationCases.active, true)
            ),
            orderBy: [desc(moderationCases.createdAt)],
        });
    }

    /**
     * Get all notes for a user
     */
    static async getUserNotes(userId: string) {
        return await DrizzleClient.query.moderationCases.findMany({
            where: and(
                eq(moderationCases.userId, BigInt(userId)),
                eq(moderationCases.type, CaseType.NOTE)
            ),
            orderBy: [desc(moderationCases.createdAt)],
        });
    }

    /**
     * Clear/resolve a warning
     */
    static async clearCase(options: ClearCaseOptions) {
        const [updatedCase] = await DrizzleClient.update(moderationCases)
            .set({
                active: false,
                resolvedAt: new Date(),
                resolvedBy: BigInt(options.clearedBy),
                resolvedReason: options.reason || 'Manually cleared',
            })
            .where(eq(moderationCases.caseId, options.caseId))
            .returning();

        return updatedCase;
    }

    /**
     * Search cases with various filters
     */
    static async searchCases(filter: SearchCasesFilter) {
        const conditions = [];

        if (filter.userId) {
            conditions.push(eq(moderationCases.userId, BigInt(filter.userId)));
        }

        if (filter.moderatorId) {
            conditions.push(eq(moderationCases.moderatorId, BigInt(filter.moderatorId)));
        }

        if (filter.type) {
            conditions.push(eq(moderationCases.type, filter.type));
        }

        if (filter.active !== undefined) {
            conditions.push(eq(moderationCases.active, filter.active));
        }

        const whereClause = conditions.length > 0 ? and(...conditions) : undefined;

        return await DrizzleClient.query.moderationCases.findMany({
            where: whereClause,
            orderBy: [desc(moderationCases.createdAt)],
            limit: filter.limit || 50,
            offset: filter.offset || 0,
        });
    }

    /**
     * Get total count of active warnings for a user (useful for auto-timeout)
     */
    static async getActiveWarningCount(userId: string): Promise<number> {
        const warnings = await this.getUserWarnings(userId);
        return warnings.length;
    }
}
