
import { describe, it, expect, mock, beforeEach } from "bun:test";
import { moderationService } from "@shared/modules/moderation/moderation.service";
import { moderationCases } from "@db/schema";
import { CaseType } from "@shared/lib/constants";

// Mock Drizzle Functions
const mockFindFirst = mock();
const mockFindMany = mock();
const mockInsert = mock();
const mockUpdate = mock();
const mockValues = mock();
const mockReturning = mock();
const mockSet = mock();
const mockWhere = mock();



// Mock View
const mockGetUserWarningEmbed = mock(() => ({}));
mock.module("@/modules/moderation/moderation.view", () => ({
    getUserWarningEmbed: mockGetUserWarningEmbed
}));

// Mock DrizzleClient
mock.module("@shared/db/DrizzleClient", () => ({
    DrizzleClient: {
        query: {
            moderationCases: {
                findFirst: mockFindFirst,
                findMany: mockFindMany,
            },
        },
        insert: mockInsert,
        update: mockUpdate,
    }
}));

// Setup chains
mockInsert.mockReturnValue({ values: mockValues });
mockValues.mockReturnValue({ returning: mockReturning });
mockUpdate.mockReturnValue({ set: mockSet });
mockSet.mockReturnValue({ where: mockWhere });
mockWhere.mockReturnValue({ returning: mockReturning });

describe("moderationService", () => {
    beforeEach(() => {
        mockFindFirst.mockReset();
        mockFindMany.mockReset();
        mockInsert.mockClear();
        mockUpdate.mockClear();
        mockValues.mockClear();
        mockReturning.mockClear();
        mockSet.mockClear();
        mockWhere.mockClear();
        mockGetUserWarningEmbed.mockClear();
    });

    describe("issueWarning", () => {
        const defaultOptions = {
            userId: "123456789",
            username: "testuser",
            moderatorId: "987654321",
            moderatorName: "mod",
            reason: "test reason",
            guildName: "Test Guild"
        };

        it("should issue a warning and attempt to DM the user", async () => {
            mockFindFirst.mockResolvedValue({ caseId: "CASE-0001" });
            mockReturning.mockResolvedValue([{ caseId: "CASE-0002" }]);
            mockFindMany.mockResolvedValue([{ type: CaseType.WARN, active: true }]); // 1 warning total

            const mockDmTarget = { send: mock() };

            const result = await moderationService.issueWarning({
                ...defaultOptions,
                dmTarget: mockDmTarget
            });

            expect(result.moderationCase).toBeDefined();
            expect(result.warningCount).toBe(1);
            expect(mockDmTarget.send).toHaveBeenCalled();
            expect(mockGetUserWarningEmbed).toHaveBeenCalled();
        });

        it("should not DM if dmOnWarn is false", async () => {
            mockFindFirst.mockResolvedValue({ caseId: "CASE-0001" });
            mockReturning.mockResolvedValue([{ caseId: "CASE-0002" }]);
            mockFindMany.mockResolvedValue([]);

            const mockDmTarget = { send: mock() };

            await moderationService.issueWarning({
                ...defaultOptions,
                dmTarget: mockDmTarget,
                config: { dmOnWarn: false }
            });

            expect(mockDmTarget.send).not.toHaveBeenCalled();
        });

        it("should trigger auto-timeout when threshold is reached", async () => {
            mockFindFirst.mockResolvedValue({ caseId: "CASE-0001" });
            mockReturning.mockResolvedValue([{ caseId: "CASE-0002" }]);
            // Simulate 3 warnings (threshold is 3)
            mockFindMany.mockResolvedValue([{}, {}, {}]);

            const mockTimeoutTarget = { timeout: mock() };

            const result = await moderationService.issueWarning({
                ...defaultOptions,
                timeoutTarget: mockTimeoutTarget,
                config: { autoTimeoutThreshold: 3 }
            });

            expect(result.autoTimeoutIssued).toBe(true);
            expect(mockTimeoutTarget.timeout).toHaveBeenCalledWith(86400000, expect.stringContaining("3 warnings"));
            // Should create two cases: one for warn, one for timeout
            expect(mockInsert).toHaveBeenCalledTimes(2);
        });

        it("should not timeout if threshold is not reached", async () => {
            mockFindFirst.mockResolvedValue({ caseId: "CASE-0001" });
            mockReturning.mockResolvedValue([{ caseId: "CASE-0002" }]);
            // Simulate 2 warnings (threshold is 3)
            mockFindMany.mockResolvedValue([{}, {}]);

            const mockTimeoutTarget = { timeout: mock() };

            const result = await moderationService.issueWarning({
                ...defaultOptions,
                timeoutTarget: mockTimeoutTarget
            });

            expect(result.autoTimeoutIssued).toBe(false);
            expect(mockTimeoutTarget.timeout).not.toHaveBeenCalled();
            expect(mockInsert).toHaveBeenCalledTimes(1);
        });
    });

    describe("createCase", () => {
        it("should create a new moderation case with correct values", async () => {
            mockFindFirst.mockResolvedValue({ caseId: "CASE-0001" });
            const mockNewCase = {
                caseId: "CASE-0002",
                type: CaseType.WARN,
                userId: 123456789n,
                username: "testuser",
                moderatorId: 987654321n,
                moderatorName: "mod",
                reason: "test reason",
                metadata: {},
                active: true
            };
            mockReturning.mockResolvedValue([mockNewCase]);

            const result = await moderationService.createCase({
                type: CaseType.WARN,
                userId: "123456789",
                username: "testuser",
                moderatorId: "987654321",
                moderatorName: "mod",
                reason: "test reason"
            });

            expect(result?.caseId).toBe("CASE-0002");
            expect(mockInsert).toHaveBeenCalled();
            expect(mockValues).toHaveBeenCalledWith(expect.objectContaining({
                caseId: "CASE-0002",
                type: CaseType.WARN,
                userId: 123456789n,
                reason: "test reason"
            }));
        });

        it("should set active to false for non-warn types", async () => {
            mockFindFirst.mockResolvedValue(undefined);
            mockReturning.mockImplementation((values) => [values]); // Simplified mock

            const result = await moderationService.createCase({
                type: CaseType.BAN,
                userId: "123456789",
                username: "testuser",
                moderatorId: "987654321",
                moderatorName: "mod",
                reason: "test reason"
            });

            expect(mockValues).toHaveBeenCalledWith(expect.objectContaining({
                active: false
            }));
        });
    });

    describe("getCaseById", () => {
        it("should return a case by its ID", async () => {
            const mockCase = { caseId: "CASE-0001", reason: "test" };
            mockFindFirst.mockResolvedValue(mockCase);

            const result = await moderationService.getCaseById("CASE-0001");
            expect(result).toEqual(mockCase as any);
        });
    });

    describe("getUserCases", () => {
        it("should return all cases for a user", async () => {
            const mockCases = [{ caseId: "CASE-0001" }, { caseId: "CASE-0002" }];
            mockFindMany.mockResolvedValue(mockCases);

            const result = await moderationService.getUserCases("123456789");
            expect(result).toHaveLength(2);
            expect(mockFindMany).toHaveBeenCalled();
        });
    });

    describe("clearCase", () => {
        it("should update a case to be inactive and resolved", async () => {
            const mockUpdatedCase = { caseId: "CASE-0001", active: false };
            mockReturning.mockResolvedValue([mockUpdatedCase]);

            const result = await moderationService.clearCase({
                caseId: "CASE-0001",
                clearedBy: "987654321",
                clearedByName: "mod",
                reason: "resolved"
            });

            expect(result?.active).toBe(false);
            expect(mockUpdate).toHaveBeenCalled();
            expect(mockSet).toHaveBeenCalledWith(expect.objectContaining({
                active: false,
                resolvedBy: 987654321n,
                resolvedReason: "resolved"
            }));
        });
    });

    describe("getActiveWarningCount", () => {
        it("should return the number of active warnings", async () => {
            mockFindMany.mockResolvedValue([
                { id: 1n, type: CaseType.WARN, active: true },
                { id: 2n, type: CaseType.WARN, active: true }
            ]);

            const count = await moderationService.getActiveWarningCount("123456789");
            expect(count).toBe(2);
        });

        it("should return 0 if no active warnings", async () => {
            mockFindMany.mockResolvedValue([]);
            const count = await moderationService.getActiveWarningCount("123456789");
            expect(count).toBe(0);
        });
    });
});
