import { funSendWebPush, prisma } from "@/module/_global";
import { funGetUserById } from "@/module/auth";
import { createLogUserMobile } from "@/module/user";
import _ from "lodash";
import moment from "moment";
import { NextResponse } from "next/server";
import { sendFCMNotificationMany } from "../../../../../../../../xsendMany";

const APPROVER_ROLES = ['supadmin', 'developer'];

async function getApproverStatus(userId: string): Promise<boolean> {
    const user = await prisma.user.findUnique({
        where: { id: userId },
        select: {
            isApprover: true,
            UserRole: { select: { id: true } }
        }
    });
    if (!user) return false;
    return user.isApprover || APPROVER_ROLES.includes(user.UserRole.id);
}

async function recalculateTaskStatus(idProject: string) {
    const tasks = await prisma.divisionProjectTask.findMany({
        where: { isActive: true, idProject },
        select: { status: true }
    });

    const semua = tasks.length;
    const selesai = tasks.filter((t) => t.status === 1).length;
    const prosess = semua === 0 ? 0 : Math.ceil((selesai / semua) * 100);

    let statusProject = 1;
    if (prosess === 100) statusProject = 2;
    else if (prosess === 0) statusProject = 0;

    await prisma.divisionProject.update({
        where: { id: idProject },
        data: { status: statusProject }
    });
}

type NotifTarget = {
    idUserTo: string;
    tokens: string[];
    subscription: string | undefined;
}

async function sendNotification({
    targets,
    idUserFrom,
    idContent,
    category,
    title,
    desc,
}: {
    targets: NotifTarget[];
    idUserFrom: string;
    idContent: string;
    category: string;
    title: string;
    desc: string;
}) {
    const filtered = targets.filter((t) => t.idUserTo !== idUserFrom);
    const unique = _.uniqBy(filtered, 'idUserTo');

    if (unique.length === 0) return;

    await prisma.notifications.createMany({
        data: unique.map((t) => ({
            idUserTo: t.idUserTo,
            idUserFrom,
            category,
            idContent,
            title,
            desc,
        }))
    });

    const tokens = [...new Set(unique.flatMap((t) => t.tokens))].filter(Boolean);
    if (tokens.length > 0) {
        await sendFCMNotificationMany({
            token: tokens,
            title,
            body: desc,
            data: { id: idContent, category, content: idContent }
        });
    }

    const subs = unique
        .filter((t): t is typeof t & { subscription: string } => Boolean(t.subscription))
        .map((t) => ({ idUser: t.idUserTo, subscription: t.subscription }));
    if (subs.length > 0) {
        await funSendWebPush({ sub: subs, message: { title, body: desc } });
    }
}

async function getApproversForDivision(idVillage: string, idDivision: string): Promise<NotifTarget[]> {
    const division = await prisma.division.findUnique({
        where: { id: idDivision },
        select: { idGroup: true }
    });
    const idGroup = division?.idGroup;

    const [globalApprovers, divisionAdmins] = await Promise.all([
        prisma.user.findMany({
            where: {
                isActive: true,
                idVillage,
                OR: [
                    { isApprover: true, idGroup },
                    { UserRole: { id: 'supadmin' } }
                ]
            },
            select: {
                id: true,
                TokenDeviceUser: { select: { token: true } },
                Subscribe: { select: { subscription: true } }
            }
        }),
        prisma.divisionMember.findMany({
            where: { idDivision, isAdmin: true, isActive: true },
            select: {
                User: {
                    select: {
                        id: true,
                        TokenDeviceUser: { select: { token: true } },
                        Subscribe: { select: { subscription: true } }
                    }
                }
            }
        })
    ]);

    const fromGlobal = globalApprovers.map((u) => ({
        idUserTo: u.id,
        tokens: u.TokenDeviceUser.map((t) => t.token),
        subscription: u.Subscribe?.subscription ?? undefined,
    }));

    const fromAdmin = divisionAdmins.map((m) => ({
        idUserTo: m.User.id,
        tokens: m.User.TokenDeviceUser.map((t) => t.token),
        subscription: m.User.Subscribe?.subscription ?? undefined,
    }));

    return _.uniqBy([...fromGlobal, ...fromAdmin], 'idUserTo');
}

async function getUserNotifTarget(userId: string): Promise<NotifTarget | null> {
    const user = await prisma.user.findUnique({
        where: { id: userId },
        select: {
            id: true,
            TokenDeviceUser: { select: { token: true } },
            Subscribe: { select: { subscription: true } }
        }
    });
    if (!user) return null;
    return {
        idUserTo: user.id,
        tokens: user.TokenDeviceUser.map((t) => t.token),
        subscription: user.Subscribe?.subscription ?? undefined,
    };
}


// GET — Riwayat approval task divisi
export async function GET(request: Request, context: { params: { id: string } }) {
    try {
        const { id } = context.params;
        const { searchParams } = new URL(request.url);
        const user = searchParams.get("user");

        const userMobile = await funGetUserById({ id: String(user) });
        if (!userMobile.id) {
            return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 200 });
        }

        const task = await prisma.divisionProjectTask.count({ where: { id, isActive: true } });
        if (task === 0) {
            return NextResponse.json({ success: false, message: "Tugas tidak ditemukan" }, { status: 200 });
        }

        const data = await prisma.divisionProjectTaskApproval.findMany({
            where: { idTask: id },
            orderBy: { createdAt: "desc" },
            select: {
                id: true,
                status: true,
                note: true,
                createdAt: true,
                Submitter: { select: { name: true } },
                Approver: { select: { name: true } },
            }
        });

        const formatted = data.map((v) => ({
            id: v.id,
            status: v.status,
            note: v.note,
            createdAt: moment(v.createdAt).format("DD MMM YYYY, HH:mm"),
            submitter: { name: v.Submitter.name },
            approver: v.Approver ? { name: v.Approver.name } : null,
        }));

        return NextResponse.json({ success: true, message: "Riwayat approval berhasil ditemukan", data: formatted }, { status: 200 });
    } catch (error) {
        console.error(error);
        return NextResponse.json({ success: false, message: "Gagal mendapatkan riwayat approval (error: 500)", reason: (error as Error).message }, { status: 500 });
    }
}


// POST — Ajukan selesai
export async function POST(request: Request, context: { params: { id: string } }) {
    try {
        const { id } = context.params;
        const { user } = await request.json();

        const userMobile = await funGetUserById({ id: String(user) });
        if (!userMobile.id) {
            return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 200 });
        }

        const task = await prisma.divisionProjectTask.findUnique({
            where: { id, isActive: true },
            select: { id: true, status: true, idProject: true, idDivision: true, title: true }
        });

        if (!task) {
            return NextResponse.json({ success: false, message: "Tugas tidak ditemukan" }, { status: 200 });
        }

        if (task.status !== 0) {
            return NextResponse.json({ success: false, message: "Hanya tugas berstatus 'Belum Selesai' yang bisa diajukan" }, { status: 200 });
        }

        const pendingApproval = await prisma.divisionProjectTaskApproval.count({
            where: { idTask: id, status: 0 }
        });

        if (pendingApproval > 0) {
            return NextResponse.json({ success: false, message: "Tugas sudah dalam proses menunggu persetujuan" }, { status: 200 });
        }

        await prisma.$transaction([
            prisma.divisionProjectTaskApproval.create({
                data: { idTask: id, idUser: userMobile.id, status: 0 }
            }),
            prisma.divisionProjectTask.update({
                where: { id },
                data: { status: 2 }
            })
        ]);

        await recalculateTaskStatus(task.idProject);

        const approverTargets = await getApproversForDivision(String(userMobile.idVillage), task.idDivision);
        await sendNotification({
            targets: approverTargets,
            idUserFrom: userMobile.id,
            idContent: task.idProject,
            category: `division/${task.idDivision}/task`,
            title: 'Pengajuan Penyelesaian Tugas',
            desc: task.title,
        });

        await createLogUserMobile({ act: 'CREATE', desc: 'User mengajukan task divisi untuk persetujuan', table: 'divisionProjectTaskApproval', data: id, user: userMobile.id });

        return NextResponse.json({ success: true, message: "Tugas berhasil diajukan untuk persetujuan" }, { status: 200 });
    } catch (error) {
        console.error(error);
        return NextResponse.json({ success: false, message: "Gagal mengajukan tugas (error: 500)", reason: (error as Error).message }, { status: 500 });
    }
}


// PUT — Setujui atau Tolak
export async function PUT(request: Request, context: { params: { id: string } }) {
    try {
        const { id } = context.params;
        const { user, action, note } = await request.json();

        if (!['approve', 'reject'].includes(action)) {
            return NextResponse.json({ success: false, message: "Action tidak valid, gunakan 'approve' atau 'reject'" }, { status: 200 });
        }

        const userMobile = await funGetUserById({ id: String(user) });
        if (!userMobile.id) {
            return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 200 });
        }

        const taskForAuth = await prisma.divisionProjectTask.findUnique({
            where: { id, isActive: true },
            select: { idDivision: true }
        });
        if (!taskForAuth) {
            return NextResponse.json({ success: false, message: "Tugas tidak ditemukan" }, { status: 200 });
        }

        const [division, userFull, isDivAdmin] = await Promise.all([
            prisma.division.findUnique({
                where: { id: taskForAuth.idDivision },
                select: { idGroup: true, idVillage: true }
            }),
            prisma.user.findUnique({
                where: { id: userMobile.id },
                select: { isApprover: true, idGroup: true, idVillage: true, UserRole: { select: { id: true } } }
            }),
            prisma.divisionMember.count({
                where: { idDivision: taskForAuth.idDivision, idUser: userMobile.id, isAdmin: true, isActive: true }
            })
        ]);

        const isSupadmin = APPROVER_ROLES.includes(userFull?.UserRole?.id ?? '');
        const isGroupApprover = !!(userFull?.isApprover &&
            userFull.idVillage === division?.idVillage &&
            userFull.idGroup === division?.idGroup);

        if (!isSupadmin && !isGroupApprover && isDivAdmin === 0) {
            return NextResponse.json({ success: false, message: "Anda tidak memiliki izin untuk menyetujui atau menolak tugas" }, { status: 200 });
        }

        const task = await prisma.divisionProjectTask.findUnique({
            where: { id, isActive: true },
            select: { id: true, status: true, idProject: true, idDivision: true, title: true }
        });

        if (!task) {
            return NextResponse.json({ success: false, message: "Tugas tidak ditemukan" }, { status: 200 });
        }

        if (task.status !== 2) {
            return NextResponse.json({ success: false, message: "Tugas tidak sedang menunggu persetujuan" }, { status: 200 });
        }

        const pendingApproval = await prisma.divisionProjectTaskApproval.findFirst({
            where: { idTask: id, status: 0 },
            orderBy: { createdAt: "desc" },
            select: { id: true, idUser: true }
        });

        if (!pendingApproval) {
            return NextResponse.json({ success: false, message: "Data persetujuan pending tidak ditemukan" }, { status: 200 });
        }

        if (action === 'approve') {
            await prisma.$transaction([
                prisma.divisionProjectTaskApproval.update({
                    where: { id: pendingApproval.id },
                    data: { status: 1, idApprover: userMobile.id }
                }),
                prisma.divisionProjectTask.update({
                    where: { id },
                    data: { status: 1 }
                })
            ]);

            await recalculateTaskStatus(task.idProject);

            const [submitterTarget, approverTargets] = await Promise.all([
                getUserNotifTarget(pendingApproval.idUser),
                getApproversForDivision(String(userMobile.idVillage), task.idDivision),
            ]);
            const notifTargets = _.uniqBy([
                ...(submitterTarget ? [submitterTarget] : []),
                ...approverTargets,
            ], 'idUserTo');
            await sendNotification({
                targets: notifTargets,
                idUserFrom: userMobile.id,
                idContent: task.idProject,
                category: `division/${task.idDivision}/task`,
                title: 'Tugas Disetujui',
                desc: task.title,
            });

            await createLogUserMobile({ act: 'UPDATE', desc: 'Approver menyetujui task divisi', table: 'divisionProjectTaskApproval', data: id, user: userMobile.id });

            return NextResponse.json({ success: true, message: "Tugas berhasil disetujui" }, { status: 200 });
        }

        if (!note || String(note).trim() === '') {
            return NextResponse.json({ success: false, message: "Alasan penolakan wajib diisi" }, { status: 200 });
        }

        await prisma.$transaction([
            prisma.divisionProjectTaskApproval.update({
                where: { id: pendingApproval.id },
                data: { status: 2, idApprover: userMobile.id, note: String(note).trim() }
            }),
            prisma.divisionProjectTask.update({
                where: { id },
                data: { status: 0 }
            })
        ]);

        await recalculateTaskStatus(task.idProject);

        const [submitterTarget, approverTargets] = await Promise.all([
            getUserNotifTarget(pendingApproval.idUser),
            getApproversForDivision(String(userMobile.idVillage), task.idDivision),
        ]);
        const notifTargets = _.uniqBy([
            ...(submitterTarget ? [submitterTarget] : []),
            ...approverTargets,
        ], 'idUserTo');
        await sendNotification({
            targets: notifTargets,
            idUserFrom: userMobile.id,
            idContent: task.idProject,
            category: `division/${task.idDivision}/task`,
            title: 'Tugas Ditolak',
            desc: task.title,
        });

        await createLogUserMobile({ act: 'UPDATE', desc: 'Approver menolak task divisi', table: 'divisionProjectTaskApproval', data: id, user: userMobile.id });

        return NextResponse.json({ success: true, message: "Tugas berhasil ditolak" }, { status: 200 });

    } catch (error) {
        console.error(error);
        return NextResponse.json({ success: false, message: "Gagal memproses persetujuan (error: 500)", reason: (error as Error).message }, { status: 500 });
    }
}
