import { prisma } from "@/module/_global";
import cors from "@elysiajs/cors";
import { swagger } from "@elysiajs/swagger";
import Elysia, { t } from "elysia";
import _ from "lodash";
import moment from "moment";
import "moment/locale/id";

const CACHE_TTL_MS = 60_000;
let apiKeyCache: Set<string> = new Set();
let cacheExpiresAt = 0;

async function isValidApiKey(incoming: string): Promise<boolean> {
   const now = Date.now();
   if (now > cacheExpiresAt) {
      const rows = await prisma.apiKey.findMany({ where: { isActive: true }, select: { key: true } });
      apiKeyCache = new Set(rows.map((r) => r.key));
      cacheExpiresAt = now + CACHE_TTL_MS;
   }
   return apiKeyCache.has(incoming);
}

const AiServer = new Elysia({ prefix: "/api/ai" })
   .use(cors({
      origin: "*",
      methods: ["GET", "OPTIONS"],
      allowedHeaders: ["Content-Type", "x-api-key"],
   }))
   .use(swagger({
      path: "/docs",
      documentation: {
         info: {
            title: "Desa Plus - Jenna Perangkat Desa API",
            version: "1.0.0",
            description: "API untuk kebutuhan integrasi Jenna Perangkat Desa — data desa, divisi, proyek, dll.",
         },
         components: {
            securitySchemes: {
               ApiKeyAuth: {
                  type: "apiKey",
                  in: "header",
                  name: "x-api-key",
               },
            },
         },
         security: [{ ApiKeyAuth: [] }],
      },
   }))
   .onBeforeHandle(async ({ request, set, path }) => {
      if (path.startsWith("/api/ai/docs")) return;

      const incoming = request.headers.get("x-api-key");
      if (!incoming || !(await isValidApiKey(incoming))) {
         set.status = 401;
         return { success: false, message: "Unauthorized" };
      }
   })

   // ─── ANNOUNCEMENT ────────────────────────────────────────────────────────

   .get("/announcement", async ({ query, set }) => {
      try {
         const { search, page, get, desa, active } = query;

         const getFix = !get || _.isNaN(Number(get)) ? 10 : Number(get);
         const dataSkip = !page ? 0 : Number(page) * getFix - getFix;

         const data = await prisma.announcement.findMany({
            skip: dataSkip,
            take: getFix,
            where: {
               idVillage: String(desa),
               isActive: active === "false" ? false : true,
               title: { contains: search ?? "", mode: "insensitive" },
            },
            orderBy: { createdAt: "desc" },
         });

         return { success: true, message: "Berhasil mendapatkan pengumuman", data };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan pengumuman", reason: (error as Error).message };
      }
   }, {
      query: t.Object({
         desa: t.Optional(t.String({ description: "ID desa" })),
         search: t.Optional(t.String({ description: "Kata kunci judul" })),
         page: t.Optional(t.String({ description: "Halaman" })),
         get: t.Optional(t.String({ description: "Jumlah data per halaman" })),
         active: t.Optional(t.String({ description: "Filter aktif (true/false)" })),
      }),
      detail: { summary: "List Pengumuman", tags: ["announcement"] },
   })

   .get("/announcement/:id", async ({ params, set }) => {
      try {
         const { id } = params;

         const count = await prisma.announcement.count({ where: { id } });
         if (count === 0) {
            set.status = 404;
            return { success: false, message: "Pengumuman tidak ditemukan" };
         }

         const announcement = await prisma.announcement.findUnique({
            where: { id },
            select: { id: true, title: true, desc: true },
         });

         const announcementMember = await prisma.announcementMember.findMany({
            where: { idAnnouncement: id },
            select: {
               idGroup: true,
               idDivision: true,
               Group: { select: { name: true } },
               Division: { select: { name: true } },
            },
         });

         const member = announcementMember.map((v: any) => ({
            ..._.omit(v, ["Group", "Division"]),
            group: v.Group.name,
            division: v.Division.name,
         }));

         return { success: true, message: "Berhasil mendapatkan pengumuman", data: { ...announcement, member } };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan pengumuman", reason: (error as Error).message };
      }
   }, {
      params: t.Object({ id: t.String({ description: "ID pengumuman" }) }),
      detail: { summary: "Detail Pengumuman", tags: ["announcement"] },
   })

   // ─── BANNER ──────────────────────────────────────────────────────────────

   .get("/banner", async ({ query, set }) => {
      try {
         const { search, page, get, desa, active } = query;

         const getFix = !get || _.isNaN(Number(get)) ? 10 : Number(get);
         const dataSkip = !page ? 0 : Number(page) * getFix - getFix;

         const data = await prisma.bannerImage.findMany({
            skip: dataSkip,
            take: getFix,
            where: {
               idVillage: String(desa),
               isActive: active === "false" ? false : true,
               title: { contains: search ?? "", mode: "insensitive" },
            },
            orderBy: { createdAt: "desc" },
         });

         return { success: true, message: "Berhasil mendapatkan banner", data };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan banner", reason: (error as Error).message };
      }
   }, {
      query: t.Object({
         desa: t.Optional(t.String({ description: "ID desa" })),
         search: t.Optional(t.String({ description: "Kata kunci judul" })),
         page: t.Optional(t.String({ description: "Halaman" })),
         get: t.Optional(t.String({ description: "Jumlah data per halaman" })),
         active: t.Optional(t.String({ description: "Filter aktif (true/false)" })),
      }),
      detail: { summary: "List Banner", tags: ["banner"] },
   })

   .get("/banner/:id", async ({ params, set }) => {
      try {
         const { id } = params;
         const data = await prisma.bannerImage.findUnique({ where: { id: String(id) } });
         return { success: true, message: "Berhasil mendapatkan banner", data };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan banner", reason: (error as Error).message };
      }
   }, {
      params: t.Object({ id: t.String({ description: "ID banner" }) }),
      detail: { summary: "Detail Banner", tags: ["banner"] },
   })

   // ─── CALENDAR ────────────────────────────────────────────────────────────

   .get("/calendar", async ({ query, set }) => {
      try {
         const { division, date, desa, active, search, page, get } = query;

         const getFix = !get || _.isNaN(Number(get)) ? 10 : Number(get);
         const dataSkip = !page ? 0 : Number(page) * getFix - getFix;

         let kondisi: any = {};

         const baseCalendar = {
            title: { contains: search ?? "", mode: "insensitive" },
            isActive: active === "false" ? false : true,
            Division: { idVillage: String(desa) },
         };

         if (division) {
            kondisi = { idDivision: division, ...(date && { dateStart: new Date(date) }), DivisionCalendar: baseCalendar };
         } else {
            kondisi = { ...(date && { dateStart: new Date(date) }), DivisionCalendar: baseCalendar };
         }

         const data = await prisma.divisionCalendarReminder.findMany({
            where: kondisi,
            skip: dataSkip,
            take: getFix,
            select: {
               id: true,
               dateStart: true,
               timeStart: true,
               timeEnd: true,
               createdAt: true,
               DivisionCalendar: {
                  select: {
                     isActive: true,
                     title: true,
                     desc: true,
                     User: { select: { name: true } },
                  },
               },
            },
            orderBy: [{ dateStart: "asc" }, { timeStart: "asc" }, { timeEnd: "asc" }],
         });

         const result = data.map((v: any) => ({
            ..._.omit(v, ["DivisionCalendar"]),
            title: v.DivisionCalendar.title,
            desc: v.DivisionCalendar.desc,
            createdBy: v.DivisionCalendar.User.name,
            isActive: v.DivisionCalendar.isActive,
            timeStart: moment.utc(v.timeStart).format("HH:mm"),
            timeEnd: moment.utc(v.timeEnd).format("HH:mm"),
         }));

         return { success: true, message: "Berhasil mendapatkan kalender", data: result };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan kalender", reason: (error as Error).message };
      }
   }, {
      query: t.Object({
         desa: t.Optional(t.String({ description: "ID desa" })),
         division: t.Optional(t.String({ description: "ID divisi" })),
         date: t.Optional(t.String({ description: "Tanggal filter (ISO)" })),
         search: t.Optional(t.String({ description: "Kata kunci judul" })),
         page: t.Optional(t.String({ description: "Halaman" })),
         get: t.Optional(t.String({ description: "Jumlah data per halaman" })),
         active: t.Optional(t.String({ description: "Filter aktif (true/false)" })),
      }),
      detail: { summary: "List Kalender", tags: ["calendar"] },
   })

   .get("/calendar/:id", async ({ params, set }) => {
      try {
         const { id } = params;

         const count = await prisma.divisionCalendarReminder.count({ where: { id } });
         if (count === 0) {
            set.status = 404;
            return { success: false, message: "Acara tidak ditemukan" };
         }

         const data: any = await prisma.divisionCalendarReminder.findUnique({
            where: { id },
            select: {
               id: true,
               timeStart: true,
               dateStart: true,
               timeEnd: true,
               createdAt: true,
               DivisionCalendar: {
                  select: {
                     id: true,
                     title: true,
                     desc: true,
                     linkMeet: true,
                     repeatEventTyper: true,
                     repeatValue: true,
                  },
               },
            },
         });

         const { DivisionCalendar, ...dataCalendar } = data;
         const result = {
            ...dataCalendar,
            timeStart: moment.utc(dataCalendar.timeStart).format("HH:mm"),
            timeEnd: moment.utc(dataCalendar.timeEnd).format("HH:mm"),
            title: DivisionCalendar.title,
            desc: DivisionCalendar.desc,
            linkMeet: DivisionCalendar.linkMeet,
            repeatEventTyper: DivisionCalendar.repeatEventTyper,
            repeatValue: DivisionCalendar.repeatValue,
         };

         const memberRaw = await prisma.divisionCalendarMember.findMany({
            where: { idCalendar: DivisionCalendar.id },
            select: {
               id: true,
               idUser: true,
               User: { select: { id: true, name: true, email: true, img: true } },
            },
         });

         const member = memberRaw.map((v: any) => ({
            ..._.omit(v, ["User"]),
            name: v.User.name,
            email: v.User.email,
            img: v.User.img,
         }));

         return { success: true, message: "Berhasil mendapatkan kalender", data: { ...result, member } };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan kalender", reason: (error as Error).message };
      }
   }, {
      params: t.Object({ id: t.String({ description: "ID kalender reminder" }) }),
      detail: { summary: "Detail Kalender", tags: ["calendar"] },
   })

   // ─── DISCUSSION GENERAL ──────────────────────────────────────────────────

   .get("/discussion-general", async ({ query, set }) => {
      try {
         const { group, desa, search, page, status, active, get } = query;

         const getFix = !get || _.isNaN(Number(get)) ? 10 : Number(get);
         const dataSkip = !page ? 0 : Number(page) * getFix - getFix;

         let kondisi: any = {
            isActive: active === "false" ? false : true,
            status: status === "close" ? 2 : 1,
            idVillage: String(desa),
            title: { contains: !search || search === "null" ? "" : search, mode: "insensitive" },
         };

         if (group && group !== "null") {
            kondisi.idGroup = String(group);
         }

         const data = await prisma.discussion.findMany({
            skip: dataSkip,
            take: getFix,
            where: kondisi,
            orderBy: [{ status: "desc" }, { createdAt: "desc" }],
            select: {
               id: true,
               title: true,
               desc: true,
               status: true,
               createdAt: true,
               DiscussionComment: { select: { id: true } },
               Group: { select: { name: true } },
            },
         });

         const result = data.map((v: any) => ({
            ..._.omit(v, ["DiscussionComment", "status", "Group"]),
            totalKomentar: v.DiscussionComment.length,
            status: v.status === 1 ? "Open" : "Close",
            group: v.Group.name,
         }));

         return { success: true, message: "Berhasil mendapatkan diskusi", data: result };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan diskusi", reason: (error as Error).message };
      }
   }, {
      query: t.Object({
         desa: t.Optional(t.String({ description: "ID desa" })),
         group: t.Optional(t.String({ description: "ID group" })),
         search: t.Optional(t.String({ description: "Kata kunci" })),
         page: t.Optional(t.String({ description: "Halaman" })),
         get: t.Optional(t.String({ description: "Jumlah data per halaman" })),
         status: t.Optional(t.String({ description: "Status: open/close" })),
         active: t.Optional(t.String({ description: "Filter aktif (true/false)" })),
      }),
      detail: { summary: "List Diskusi Umum", tags: ["discussion-general"] },
   })

   .get("/discussion-general/:id", async ({ params, query, set }) => {
      try {
         const { id } = params;
         const { cat, desa } = query;

         const count = await prisma.discussion.count({ where: { id, idVillage: String(desa) } });
         if (count === 0) {
            set.status = 404;
            return { success: false, message: "Diskusi tidak ditemukan" };
         }

         if (cat === "comment") {
            const data = await prisma.discussionComment.findMany({
               where: { idDiscussion: id, isActive: true },
               select: {
                  id: true,
                  comment: true,
                  createdAt: true,
                  idUser: true,
                  User: { select: { name: true, img: true } },
               },
            });
            const result = data.map((v: any) => ({
               ..._.omit(v, ["User"]),
               username: v.User.name,
               img: v.User.img,
            }));
            return { success: true, message: "Berhasil mendapatkan diskusi", data: result };
         }

         if (cat === "member") {
            const data = await prisma.discussionMember.findMany({
               where: { idDiscussion: id, isActive: true },
               select: { idUser: true, User: { select: { name: true, img: true } } },
            });
            const result = data.map((v: any) => ({
               ..._.omit(v, ["User"]),
               name: v.User.name,
               img: v.User.img,
            }));
            return { success: true, message: "Berhasil mendapatkan diskusi", data: result };
         }

         const data = await prisma.discussion.findUnique({
            where: { id, idVillage: String(desa) },
            select: {
               isActive: true,
               id: true,
               title: true,
               idGroup: true,
               desc: true,
               status: true,
               createdAt: true,
               Group: { select: { name: true } },
            },
         });

         return {
            success: true,
            message: "Berhasil mendapatkan diskusi",
            data: {
               id: data?.id,
               isActive: data?.isActive,
               idGroup: data?.idGroup,
               group: data?.Group.name,
               title: data?.title,
               desc: data?.desc,
               status: data?.status === 1 ? "Open" : "Close",
               createdAt: data?.createdAt,
            },
         };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan diskusi", reason: (error as Error).message };
      }
   }, {
      params: t.Object({ id: t.String({ description: "ID diskusi umum" }) }),
      query: t.Object({
         desa: t.Optional(t.String({ description: "ID desa" })),
         cat: t.Optional(t.String({ description: "Kategori: comment / member" })),
      }),
      detail: { summary: "Detail Diskusi Umum", tags: ["discussion-general"] },
   })

   // ─── DISCUSSION (DIVISI) ─────────────────────────────────────────────────

   .get("/discussion", async ({ query, set }) => {
      try {
         const { division, search, page, status, active, desa, get } = query;

         const getFix = !get || _.isNaN(Number(get)) ? 10 : Number(get);
         const dataSkip = !page ? 0 : Number(page) * getFix - getFix;

         let kondisi: any = {
            isActive: active === "false" ? false : true,
            status: status === "close" ? 2 : 1,
            Division: { idVillage: String(desa) },
            desc: { contains: !search || search === "null" ? "" : search, mode: "insensitive" },
         };

         if (division && division !== "null") {
            kondisi.idDivision = division;
         }

         const data = await prisma.divisionDisscussion.findMany({
            skip: dataSkip,
            take: getFix,
            where: kondisi,
            orderBy: { createdAt: "desc" },
            select: {
               id: true,
               desc: true,
               status: true,
               createdAt: true,
               idDivision: true,
               Division: { select: { name: true } },
               DivisionDisscussionComment: { select: { id: true } },
            },
         });

         const result = data.map((v: any) => ({
            ..._.omit(v, ["DivisionDisscussionComment", "status", "Division"]),
            totalKomentar: v.DivisionDisscussionComment.length,
            status: v.status === 1 ? "Open" : "Close",
            division: v.Division.name,
         }));

         return { success: true, message: "Berhasil mendapatkan diskusi", data: result };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan diskusi", reason: (error as Error).message };
      }
   }, {
      query: t.Object({
         desa: t.Optional(t.String({ description: "ID desa" })),
         division: t.Optional(t.String({ description: "ID divisi" })),
         search: t.Optional(t.String({ description: "Kata kunci" })),
         page: t.Optional(t.String({ description: "Halaman" })),
         get: t.Optional(t.String({ description: "Jumlah data per halaman" })),
         status: t.Optional(t.String({ description: "Status: open/close" })),
         active: t.Optional(t.String({ description: "Filter aktif (true/false)" })),
      }),
      detail: { summary: "List Diskusi Divisi", tags: ["discussion"] },
   })

   .get("/discussion/:id", async ({ params, set }) => {
      try {
         const { id } = params;

         const count = await prisma.divisionDisscussion.count({ where: { id } });
         if (count === 0) {
            set.status = 404;
            return { success: false, message: "Diskusi tidak ditemukan" };
         }

         const data = await prisma.divisionDisscussion.findUnique({
            where: { id },
            select: {
               isActive: true,
               id: true,
               desc: true,
               status: true,
               createdAt: true,
               idDivision: true,
               Division: { select: { name: true } },
               User: { select: { name: true } },
               DivisionDisscussionComment: {
                  select: {
                     id: true,
                     comment: true,
                     createdAt: true,
                     User: { select: { name: true, img: true } },
                  },
               },
            },
         });

         if (!data) {
            set.status = 404;
            return { success: false, message: "Diskusi tidak ditemukan" };
         }

         const komentar = data.DivisionDisscussionComment.map((c: any) => ({
            id: c.id,
            comment: c.comment,
            createdAt: c.createdAt,
            username: c.User.name,
            userimg: c.User.img,
         }));

         return {
            success: true,
            message: "Berhasil mendapatkan diskusi",
            data: {
               id: data.id,
               idDivision: data.idDivision,
               division: data.Division.name,
               isActive: data.isActive,
               desc: data.desc,
               status: data.status === 1 ? "Open" : "Close",
               createdAt: data.createdAt,
               createdBy: data.User.name,
               komentar,
            },
         };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan diskusi", reason: (error as Error).message };
      }
   }, {
      params: t.Object({ id: t.String({ description: "ID diskusi divisi" }) }),
      detail: { summary: "Detail Diskusi Divisi", tags: ["discussion"] },
   })

   // ─── DIVISION ────────────────────────────────────────────────────────────

   .get("/division", async ({ query, set }) => {
      try {
         const { desa, group, search, page, active, get } = query;

         const getFix = !get || _.isNaN(Number(get)) ? 10 : Number(get);
         const dataSkip = !page ? 0 : Number(page) * getFix - getFix;

         let kondisi: any = {
            isActive: active === "false" ? false : true,
            idVillage: String(desa),
            name: { contains: !search || search === "null" ? "" : search, mode: "insensitive" },
         };

         if (group && group !== "null") kondisi.idGroup = String(group);

         const data = await prisma.division.findMany({
            skip: dataSkip,
            take: getFix,
            where: kondisi,
            select: {
               id: true,
               name: true,
               desc: true,
               idGroup: true,
               Group: { select: { name: true } },
               DivisionMember: { where: { isActive: true }, select: { idUser: true } },
            },
            orderBy: { createdAt: "desc" },
         });

         const result = data.map((v: any) => ({
            ..._.omit(v, ["DivisionMember", "Group"]),
            group: v.Group.name,
            jumlahMember: v.DivisionMember.length,
         }));

         return { success: true, message: "Berhasil mendapatkan divisi", data: result };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan divisi", reason: (error as Error).message };
      }
   }, {
      query: t.Object({
         desa: t.Optional(t.String({ description: "ID desa" })),
         group: t.Optional(t.String({ description: "ID group" })),
         search: t.Optional(t.String({ description: "Kata kunci nama" })),
         page: t.Optional(t.String({ description: "Halaman" })),
         get: t.Optional(t.String({ description: "Jumlah data per halaman" })),
         active: t.Optional(t.String({ description: "Filter aktif (true/false)" })),
      }),
      detail: { summary: "List Divisi", tags: ["division"] },
   })

   .get("/division/report", async ({ query, set }) => {
      try {
         const { desa, group, division, "date-start": date, "date-end": dateAkhir, cat } = query;

         if (cat === "dokumen") {
            let kondisi: any = {
               isActive: true,
               category: "FILE",
               Division: { idVillage: String(desa) },
               createdAt: { gte: new Date(String(date)), lte: new Date(String(dateAkhir)) },
            };

            if (group) kondisi.Division = { idGroup: String(group) };
            if (division) kondisi.idDivision = String(division);

            const dataDokumen = await prisma.divisionDocumentFolderFile.findMany({ where: kondisi });
            const image = ["jpg", "jpeg", "png", "heic"];
            let gambar = 0, dokumen = 0;

            _.map(_.groupBy(dataDokumen, "extension"), (v: any) => {
               if (image.includes(v[0].extension)) gambar += v.length;
               else dokumen += v.length;
            });

            return { success: true, message: "Berhasil mendapatkan data", data: { gambar, dokumen } };
         }

         if (cat === "event") {
            const baseWhere = (dateFilter: any) => group
               ? { isActive: true, Division: { idGroup: String(group) }, DivisionCalendarReminder: { some: dateFilter } }
               : { isActive: true, Division: { idVillage: String(desa) }, DivisionCalendarReminder: { some: dateFilter } };

            let selesaiWhere = baseWhere({ dateStart: { gte: new Date(String(date)), lte: new Date() } });
            let comingWhere = baseWhere({ dateStart: { gt: new Date(), lte: new Date(String(dateAkhir)) } });

            if (division) {
               selesaiWhere = { ...selesaiWhere, idDivision: String(division) } as any;
               comingWhere = { ...comingWhere, idDivision: String(division) } as any;
            }

            const [selesai, akan_datang] = await Promise.all([
               prisma.divisionCalendar.count({ where: selesaiWhere }),
               prisma.divisionCalendar.count({ where: comingWhere }),
            ]);

            return { success: true, message: "Berhasil mendapatkan data", data: { selesai, akan_datang } };
         }

         // default: progress
         let kondisiProgress: any = {
            isActive: true,
            Division: group ? { idGroup: String(group) } : { idVillage: String(desa) },
            DivisionProjectTask: {
               some: {
                  dateStart: { gte: new Date(String(date)) },
                  dateEnd: { lte: new Date(String(dateAkhir)) },
               },
            },
         };

         if (division) kondisiProgress.idDivision = String(division);

         const data = await prisma.divisionProject.groupBy({ where: kondisiProgress, by: ["status"], _count: true });
         const dataStatus = [
            { name: "Segera", status: 0 },
            { name: "Dikerjakan", status: 1 },
            { name: "Selesai", status: 2 },
            { name: "Dibatalkan", status: 3 },
         ];

         const total = data.reduce((n, { _count }) => n + _count, 0);
         const result = dataStatus.reduce((acc: any, ds) => {
            const found = data.find((i: any) => i.status === ds.status);
            const raw = found ? ((found._count * 100) / total).toFixed(2) : "0";
            const fix = raw !== "100.00" ? (raw.slice(-2) === "00" ? raw.slice(0, 2) : raw) : "100";
            acc[ds.name] = fix;
            return acc;
         }, {});

         return { success: true, message: "Berhasil mendapatkan data", data: result };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan data", reason: (error as Error).message };
      }
   }, {
      query: t.Object({
         desa: t.Optional(t.String({ description: "ID desa" })),
         group: t.Optional(t.String({ description: "ID group" })),
         division: t.Optional(t.String({ description: "ID divisi" })),
         "date-start": t.Optional(t.String({ description: "Tanggal mulai (ISO)" })),
         "date-end": t.Optional(t.String({ description: "Tanggal akhir (ISO)" })),
         cat: t.Optional(t.String({ description: "Kategori: dokumen / event / (kosong = progress)" })),
      }),
      detail: { summary: "Laporan Divisi", tags: ["division"] },
   })

   .get("/division/:id", async ({ params, query, set }) => {
      try {
         const { id } = params;
         const { desa } = query;

         const data = await prisma.division.findUnique({
            where: { id: String(id), idVillage: String(desa) },
         });

         if (!data) {
            set.status = 404;
            return { success: false, message: "Divisi tidak ditemukan" };
         }

         const memberRaw = await prisma.divisionMember.findMany({
            where: { idDivision: String(id), isActive: true },
            select: {
               id: true,
               isAdmin: true,
               idUser: true,
               User: { select: { name: true, img: true } },
            },
            orderBy: { isAdmin: "desc" },
         });

         const member = memberRaw.map((v: any) => ({
            ..._.omit(v, ["User"]),
            name: v.User.name,
            img: v.User.img,
         }));

         return { success: true, message: "Berhasil mendapatkan divisi", data: { ...data, member } };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan divisi", reason: (error as Error).message };
      }
   }, {
      params: t.Object({ id: t.String({ description: "ID divisi" }) }),
      query: t.Object({
         desa: t.Optional(t.String({ description: "ID desa" })),
      }),
      detail: { summary: "Detail Divisi", tags: ["division"] },
   })

   // ─── DOCUMENT ────────────────────────────────────────────────────────────

   .get("/document", async ({ query, set }) => {
      try {
         const { division, desa, path, active, search, page, get } = query;

         const getFix = !get || _.isNaN(Number(get)) ? 10 : Number(get);
         const dataSkip = !page ? 0 : Number(page) * getFix - getFix;

         const pathFix = !path || path === "undefined" || path === "null" || path === "" ? "home" : path;

         let kondisi: any = {
            Division: { idVillage: String(desa) },
            isActive: active === "false" ? false : true,
            path: pathFix,
            name: { contains: !search || search === "null" ? "" : search, mode: "insensitive" },
         };

         if (division && division !== "null") kondisi.idDivision = String(division);

         let formatDataShare: any[] = [];

         if (pathFix === "home") {
            const dataShare = await prisma.divisionDocumentShare.findMany({
               where: { isActive: true, idDivision: String(division), DivisionDocumentFolderFile: { isActive: true } },
               select: {
                  DivisionDocumentFolderFile: {
                     select: {
                        idStorage: true,
                        id: true,
                        category: true,
                        name: true,
                        extension: true,
                        path: true,
                        User: { select: { name: true } },
                        createdAt: true,
                        updatedAt: true,
                     },
                  },
               },
               orderBy: { DivisionDocumentFolderFile: { createdAt: "desc" } },
            });

            formatDataShare = dataShare.map((v: any) => ({
               idStorage: v.DivisionDocumentFolderFile.idStorage,
               id: v.DivisionDocumentFolderFile.id,
               category: v.DivisionDocumentFolderFile.category,
               name: v.DivisionDocumentFolderFile.name,
               extension: v.DivisionDocumentFolderFile.extension,
               path: v.DivisionDocumentFolderFile.path,
               createdBy: v.DivisionDocumentFolderFile.User.name,
               createdAt: v.DivisionDocumentFolderFile.createdAt,
               updatedAt: v.DivisionDocumentFolderFile.updatedAt,
               share: true,
            }));
         }

         const data = await prisma.divisionDocumentFolderFile.findMany({
            skip: dataSkip,
            take: getFix,
            where: kondisi,
            select: {
               id: true,
               category: true,
               name: true,
               extension: true,
               idStorage: true,
               path: true,
               User: { select: { name: true } },
               createdAt: true,
               updatedAt: true,
            },
            orderBy: { createdAt: "desc" },
         });

         const allData = data.map((v: any) => ({
            ..._.omit(v, ["User"]),
            createdBy: v.User.name,
            share: false,
         }));

         if (formatDataShare.length > 0) allData.push(...formatDataShare);

         return {
            success: true,
            message: "Berhasil mendapatkan item",
            data: _.orderBy(allData, ["category", "createdAt"], ["desc", "desc"]),
         };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan item", reason: (error as Error).message };
      }
   }, {
      query: t.Object({
         desa: t.Optional(t.String({ description: "ID desa" })),
         division: t.Optional(t.String({ description: "ID divisi" })),
         path: t.Optional(t.String({ description: "Path folder (default: home)" })),
         search: t.Optional(t.String({ description: "Kata kunci nama file" })),
         page: t.Optional(t.String({ description: "Halaman" })),
         get: t.Optional(t.String({ description: "Jumlah data per halaman" })),
         active: t.Optional(t.String({ description: "Filter aktif (true/false)" })),
      }),
      detail: { summary: "List Dokumen", tags: ["document"] },
   })

   // ─── GROUP ───────────────────────────────────────────────────────────────

   .get("/group", async ({ query, set }) => {
      try {
         const { desa, active, search, page, get } = query;

         const getFix = !get || _.isNaN(Number(get)) ? 10 : Number(get);
         const dataSkip = !page ? 0 : Number(page) * getFix - getFix;

         const data = await prisma.group.findMany({
            skip: dataSkip,
            take: getFix,
            where: {
               isActive: active === "false" ? false : true,
               idVillage: String(desa),
               name: { contains: search ?? "", mode: "insensitive" },
            },
            orderBy: { name: "asc" },
         });

         return { success: true, message: "Berhasil mendapatkan grup", data };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan grup", reason: (error as Error).message };
      }
   }, {
      query: t.Object({
         desa: t.Optional(t.String({ description: "ID desa" })),
         search: t.Optional(t.String({ description: "Kata kunci nama" })),
         page: t.Optional(t.String({ description: "Halaman" })),
         get: t.Optional(t.String({ description: "Jumlah data per halaman" })),
         active: t.Optional(t.String({ description: "Filter aktif (true/false)" })),
      }),
      detail: { summary: "List Group", tags: ["group"] },
   })

   // ─── POSITION ────────────────────────────────────────────────────────────

   .get("/position", async ({ query, set }) => {
      try {
         const { desa, group, active, search, page, get } = query;

         const getFix = !get || _.isNaN(Number(get)) ? 10 : Number(get);
         const dataSkip = !page ? 0 : Number(page) * getFix - getFix;

         let kondisi: any = {
            isActive: active === "false" ? false : true,
            Group: { idVillage: String(desa) },
            name: { contains: search ?? "", mode: "insensitive" },
         };

         if (group && group !== "null") kondisi.idGroup = String(group);

         const positions = await prisma.position.findMany({
            skip: dataSkip,
            take: getFix,
            where: kondisi,
            select: {
               id: true,
               name: true,
               idGroup: true,
               isActive: true,
               createdAt: true,
               updatedAt: true,
               Group: { select: { name: true } },
            },
            orderBy: { name: "asc" },
         });

         const result = positions.map((v: any) => ({
            ..._.omit(v, ["Group"]),
            group: v.Group.name,
         }));

         return { success: true, message: "Berhasil mendapatkan jabatan", data: result };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan jabatan", reason: (error as Error).message };
      }
   }, {
      query: t.Object({
         desa: t.Optional(t.String({ description: "ID desa" })),
         group: t.Optional(t.String({ description: "ID group" })),
         search: t.Optional(t.String({ description: "Kata kunci nama" })),
         page: t.Optional(t.String({ description: "Halaman" })),
         get: t.Optional(t.String({ description: "Jumlah data per halaman" })),
         active: t.Optional(t.String({ description: "Filter aktif (true/false)" })),
      }),
      detail: { summary: "List Jabatan", tags: ["position"] },
   })

   // ─── PROJECT ─────────────────────────────────────────────────────────────

   .get("/project", async ({ query, set }) => {
      try {
         const { desa, search, status, group, page, get } = query;

         const getFix = !get || _.isNaN(Number(get)) ? 10 : Number(get);
         const dataSkip = !page ? 0 : Number(page) * getFix - getFix;

         let kondisi: any = {
            isActive: true,
            idVillage: String(desa),
            title: { contains: !search || search === "null" ? "" : search, mode: "insensitive" },
         };

         if (status && status !== "null") {
            const statusMap: Record<string, number> = { segera: 0, dikerjakan: 1, selesai: 2, batal: 3 };
            kondisi.status = statusMap[status] ?? 0;
         }

         if (group && group !== "null") kondisi.idGroup = String(group);

         const data = await prisma.project.findMany({
            skip: dataSkip,
            take: getFix,
            where: kondisi,
            select: {
               id: true,
               idGroup: true,
               title: true,
               desc: true,
               status: true,
               ProjectMember: { where: { isActive: true }, select: { idUser: true } },
               ProjectTask: { where: { isActive: true }, select: { title: true, status: true } },
               Group: { select: { name: true } },
            },
            orderBy: { createdAt: "desc" },
         });

         const result = data.map((v: any) => ({
            ..._.omit(v, ["ProjectMember", "ProjectTask", "status", "Group"]),
            group: v.Group.name,
            status: v.status === 1 ? "dikerjakan" : v.status === 2 ? "selesai" : v.status === 3 ? "batal" : "segera",
            progress: _.ceil((v.ProjectTask.filter((i: any) => i.status === 1).length * 100) / v.ProjectTask.length),
            member: v.ProjectMember.length,
         }));

         return { success: true, message: "Berhasil mendapatkan kegiatan", data: result };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan kegiatan", reason: (error as Error).message };
      }
   }, {
      query: t.Object({
         desa: t.Optional(t.String({ description: "ID desa" })),
         group: t.Optional(t.String({ description: "ID group" })),
         search: t.Optional(t.String({ description: "Kata kunci judul" })),
         page: t.Optional(t.String({ description: "Halaman" })),
         get: t.Optional(t.String({ description: "Jumlah data per halaman" })),
         status: t.Optional(t.String({ description: "Status: segera/dikerjakan/selesai/batal" })),
      }),
      detail: { summary: "List Kegiatan", tags: ["project"] },
   })

   .get("/project/:id", async ({ params, query, set }) => {
      try {
         const { id } = params;
         const { cat } = query;

         const data = await prisma.project.findUnique({
            where: { id: String(id) },
            select: {
               id: true, idVillage: true, idGroup: true, title: true, status: true,
               desc: true, reason: true, report: true, isActive: true,
               Group: { select: { name: true } },
            },
         });

         if (!data) {
            set.status = 404;
            return { success: false, message: "Kegiatan tidak ditemukan" };
         }

         if (cat === "data") {
            const tasks = await prisma.projectTask.findMany({ where: { isActive: true, idProject: String(id) }, orderBy: { updatedAt: "desc" } });
            const selesai = _.filter(tasks, { status: 1 }).length;
            const progress = Math.ceil((selesai / tasks.length) * 100);
            return {
               success: true, message: "Berhasil mendapatkan kegiatan", data: {
                  id: data.id, idVillage: data.idVillage, idGroup: data.idGroup, group: data.Group.name,
                  title: data.title, status: data.status === 3 ? "batal" : data.status === 2 ? "selesai" : data.status === 1 ? "dikerjakan" : "segera",
                  desc: data.desc, reason: data.reason, report: data.report, isActive: data.isActive,
                  progress: _.isNaN(progress) ? 0 : progress,
               },
            };
         }

         if (cat === "task") {
            const tasks = await prisma.projectTask.findMany({
               where: { isActive: true, idProject: String(id) },
               select: { id: true, title: true, desc: true, status: true, dateStart: true, dateEnd: true, createdAt: true },
               orderBy: { createdAt: "asc" },
            });
            return {
               success: true, message: "Berhasil mendapatkan kegiatan",
               data: _.orderBy(tasks.map((v: any) => ({
                  ..._.omit(v, ["dateStart", "dateEnd", "createdAt", "status"]),
                  status: v.status === 1 ? "selesai" : "belum selesai",
                  dateStart: moment(v.dateStart).format("DD-MM-YYYY"),
                  dateEnd: moment(v.dateEnd).format("DD-MM-YYYY"),
                  createdAt: moment(v.createdAt).format("DD-MM-YYYY HH:mm"),
               })), "createdAt", "asc"),
            };
         }

         if (cat === "file") {
            const files = await prisma.projectFile.findMany({
               where: { isActive: true, idProject: String(id) },
               orderBy: { createdAt: "asc" },
               select: { id: true, name: true, extension: true, idStorage: true },
            });
            return { success: true, message: "Berhasil mendapatkan kegiatan", data: files };
         }

         if (cat === "member") {
            const members = await prisma.projectMember.findMany({
               where: { isActive: true, idProject: String(id) },
               select: { id: true, idUser: true, User: { select: { name: true, email: true, img: true, Position: { select: { name: true } } } } },
            });
            return {
               success: true, message: "Berhasil mendapatkan kegiatan",
               data: members.map((v: any) => ({
                  ..._.omit(v, ["User"]),
                  name: v.User.name, email: v.User.email, img: v.User.img, position: v.User.Position.name,
               })),
            };
         }

         if (cat === "link") {
            const links = await prisma.projectLink.findMany({ where: { isActive: true, idProject: String(id) }, orderBy: { createdAt: "asc" } });
            return { success: true, message: "Berhasil mendapatkan kegiatan", data: links };
         }

         return { success: true, message: "Berhasil mendapatkan kegiatan", data: null };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan kegiatan", reason: (error as Error).message };
      }
   }, {
      params: t.Object({ id: t.String({ description: "ID kegiatan" }) }),
      query: t.Object({
         cat: t.Optional(t.String({ description: "Kategori: data / task / file / member / link" })),
      }),
      detail: { summary: "Detail Kegiatan", tags: ["project"] },
   })

   // ─── TASK (DIVISI) ───────────────────────────────────────────────────────

   .get("/task", async ({ query, set }) => {
      try {
         const { desa, division, search, status, page, get } = query;

         const getFix = !get || _.isNaN(Number(get)) ? 10 : Number(get);
         const dataSkip = !page ? 0 : Number(page) * getFix - getFix;

         let kondisi: any = {
            isActive: true,
            Division: { idVillage: String(desa) },
            title: { contains: !search || search === "null" ? "" : search, mode: "insensitive" },
         };

         if (status && status !== "null") {
            const statusMap: Record<string, number> = { segera: 0, dikerjakan: 1, selesai: 2, batal: 3 };
            kondisi.status = statusMap[status] ?? 0;
         }

         if (division && division !== "null") kondisi.idDivision = String(division);

         const data = await prisma.divisionProject.findMany({
            skip: dataSkip,
            take: getFix,
            where: kondisi,
            select: {
               id: true,
               idDivision: true,
               title: true,
               desc: true,
               status: true,
               DivisionProjectTask: { where: { isActive: true }, select: { title: true, status: true } },
               DivisionProjectMember: { where: { isActive: true }, select: { idUser: true } },
               Division: { select: { name: true } },
            },
            orderBy: { createdAt: "desc" },
         });

         const result = data.map((v: any) => ({
            ..._.omit(v, ["DivisionProjectTask", "DivisionProjectMember", "status", "Division"]),
            division: v.Division.name,
            status: v.status === 1 ? "dikerjakan" : v.status === 2 ? "selesai" : v.status === 3 ? "batal" : "segera",
            progress: _.ceil((v.DivisionProjectTask.filter((i: any) => i.status === 1).length * 100) / v.DivisionProjectTask.length),
            member: v.DivisionProjectMember.length,
         }));

         return { success: true, message: "Berhasil mendapatkan tugas divisi", data: result };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan tugas divisi", reason: (error as Error).message };
      }
   }, {
      query: t.Object({
         desa: t.Optional(t.String({ description: "ID desa" })),
         division: t.Optional(t.String({ description: "ID divisi" })),
         search: t.Optional(t.String({ description: "Kata kunci judul" })),
         page: t.Optional(t.String({ description: "Halaman" })),
         get: t.Optional(t.String({ description: "Jumlah data per halaman" })),
         status: t.Optional(t.String({ description: "Status: segera/dikerjakan/selesai/batal" })),
      }),
      detail: { summary: "List Tugas Divisi", tags: ["task"] },
   })

   .get("/task/:id", async ({ params, query, set }) => {
      try {
         const { id } = params;
         const { cat } = query;

         const data = await prisma.divisionProject.findUnique({
            where: { id: String(id) },
            select: {
               id: true, idDivision: true, title: true, status: true,
               desc: true, reason: true, report: true, isActive: true,
               Division: { select: { name: true } },
            },
         });

         if (!data) {
            set.status = 404;
            return { success: false, message: "Tugas tidak ditemukan" };
         }

         if (cat === "data") {
            const tasks = await prisma.divisionProjectTask.findMany({ where: { isActive: true, idProject: String(id) }, orderBy: { updatedAt: "desc" } });
            const selesai = _.filter(tasks, { status: 1 }).length;
            const progress = Math.ceil((selesai / tasks.length) * 100);
            return {
               success: true, message: "Berhasil mendapatkan tugas divisi", data: {
                  id: data.id, idDivision: data.idDivision, division: data.Division.name,
                  title: data.title, status: data.status === 3 ? "batal" : data.status === 2 ? "selesai" : data.status === 1 ? "dikerjakan" : "segera",
                  desc: data.desc, reason: data.reason, report: data.report, isActive: data.isActive, progress,
               },
            };
         }

         if (cat === "task") {
            const tasks = await prisma.divisionProjectTask.findMany({
               where: { isActive: true, idProject: String(id) },
               select: { id: true, title: true, status: true, dateStart: true, dateEnd: true },
               orderBy: { createdAt: "asc" },
            });
            return {
               success: true, message: "Berhasil mendapatkan tugas divisi",
               data: tasks.map((v: any) => ({
                  ..._.omit(v, ["dateStart", "dateEnd", "status"]),
                  status: v.status === 1 ? "selesai" : "belum selesai",
                  dateStart: moment(v.dateStart).format("DD-MM-YYYY"),
                  dateEnd: moment(v.dateEnd).format("DD-MM-YYYY"),
               })),
            };
         }

         if (cat === "file") {
            const files = await prisma.divisionProjectFile.findMany({
               where: { isActive: true, idProject: String(id) },
               select: { id: true, ContainerFileDivision: { select: { id: true, name: true, extension: true, idStorage: true } } },
            });
            return {
               success: true, message: "Berhasil mendapatkan tugas divisi",
               data: files.map((v: any) => ({
                  ..._.omit(v, ["ContainerFileDivision"]),
                  nameInStorage: v.ContainerFileDivision.id,
                  name: v.ContainerFileDivision.name,
                  extension: v.ContainerFileDivision.extension,
                  idStorage: v.ContainerFileDivision.idStorage,
               })),
            };
         }

         if (cat === "member") {
            const members = await prisma.divisionProjectMember.findMany({
               where: { isActive: true, idProject: String(id) },
               select: { id: true, idUser: true, User: { select: { name: true, email: true, img: true, Position: { select: { name: true } } } } },
            });
            return {
               success: true, message: "Berhasil mendapatkan tugas divisi",
               data: members.map((v: any) => ({
                  ..._.omit(v, ["User"]),
                  name: v.User.name, email: v.User.email, img: v.User.img, position: v.User.Position.name,
               })),
            };
         }

         if (cat === "link") {
            const links = await prisma.divisionProjectLink.findMany({ where: { isActive: true, idProject: String(id) }, orderBy: { createdAt: "asc" } });
            return { success: true, message: "Berhasil mendapatkan tugas divisi", data: links };
         }

         return { success: true, message: "Berhasil mendapatkan tugas divisi", data: null };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan tugas divisi", reason: (error as Error).message };
      }
   }, {
      params: t.Object({ id: t.String({ description: "ID tugas divisi" }) }),
      query: t.Object({
         cat: t.Optional(t.String({ description: "Kategori: data / task / file / member / link" })),
      }),
      detail: { summary: "Detail Tugas Divisi", tags: ["task"] },
   })

   // ─── USER ────────────────────────────────────────────────────────────────

   .get("/user", async ({ query, set }) => {
      try {
         const { search, desa, group, active, page, get } = query;

         const getFix = !get || _.isNaN(Number(get)) ? 10 : Number(get);
         const dataSkip = !page ? 0 : Number(page) * getFix - getFix;

         let kondisi: any = {
            isActive: active === "false" ? false : true,
            idVillage: String(desa),
            name: { contains: search ?? "", mode: "insensitive" },
            NOT: { idUserRole: "developer" },
         };

         if (group && group !== "null") kondisi.idGroup = String(group);

         const users = await prisma.user.findMany({
            skip: dataSkip,
            take: getFix,
            where: kondisi,
            select: {
               id: true,
               idUserRole: true,
               isActive: true,
               nik: true,
               name: true,
               phone: true,
               Position: { select: { name: true } },
               Group: { select: { name: true } },
            },
            orderBy: { name: "asc" },
         });

         const result = users.map((v: any) => ({
            ..._.omit(v, ["phone", "gender", "Group", "Position"]),
            gender: v.gender === "F" ? "Perempuan" : "Laki-Laki",
            phone: "+" + v.phone,
            group: v.Group.name,
            position: v?.Position?.name,
         }));

         return { success: true, message: "Berhasil mendapatkan member", data: result };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan anggota", reason: (error as Error).message };
      }
   }, {
      query: t.Object({
         desa: t.Optional(t.String({ description: "ID desa" })),
         group: t.Optional(t.String({ description: "ID group" })),
         search: t.Optional(t.String({ description: "Kata kunci nama" })),
         page: t.Optional(t.String({ description: "Halaman" })),
         get: t.Optional(t.String({ description: "Jumlah data per halaman" })),
         active: t.Optional(t.String({ description: "Filter aktif (true/false)" })),
      }),
      detail: { summary: "List User", tags: ["user"] },
   })

   .get("/user/:id", async ({ params, set }) => {
      try {
         const { id } = params;

         const user = await prisma.user.findUnique({
            where: { id },
            select: {
               id: true, nik: true, name: true, phone: true, email: true,
               gender: true, img: true, idGroup: true, isActive: true,
               idPosition: true, createdAt: true, updatedAt: true,
               UserRole: { select: { name: true, id: true } },
               Position: { select: { name: true, id: true } },
               Group: { select: { name: true, id: true } },
            },
         });

         if (!user) {
            set.status = 404;
            return { success: false, message: "User tidak ditemukan" };
         }

         const result = _.omit(
            {
               ...user,
               gender: user.gender === "F" ? "Perempuan" : "Laki-Laki",
               phone: "+62" + user.phone,
               group: user.Group.name,
               position: user.Position?.name,
               idUserRole: user.UserRole.id,
               role: user.UserRole.name,
            },
            ["Group", "Position", "UserRole"],
         );

         return { success: true, message: "Berhasil mendapatkan anggota", data: result };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan anggota", reason: (error as Error).message };
      }
   }, {
      params: t.Object({ id: t.String({ description: "ID user" }) }),
      detail: { summary: "Detail User", tags: ["user"] },
   })

   // ─── VILLAGE ─────────────────────────────────────────────────────────────

   .get("/village", async ({ query, set }) => {
      try {
         const { active, search, page, get } = query;

         const getFix = !get || _.isNaN(Number(get)) ? 10 : Number(get);
         const dataSkip = !page ? 0 : Number(page) * getFix - getFix;

         const data = await prisma.village.findMany({
            skip: dataSkip,
            take: getFix,
            where: {
               isActive: active === "false" ? false : true,
               name: { contains: search ?? "", mode: "insensitive" },
            },
            select: { id: true, name: true, isActive: true, createdAt: true, updatedAt: true },
            orderBy: { name: "asc" },
         });

         return { success: true, message: "Berhasil mendapatkan desa", data };
      } catch (error) {
         set.status = 500;
         return { success: false, message: "Gagal mendapatkan desa", reason: (error as Error).message };
      }
   }, {
      query: t.Object({
         search: t.Optional(t.String({ description: "Kata kunci nama desa" })),
         page: t.Optional(t.String({ description: "Halaman" })),
         get: t.Optional(t.String({ description: "Jumlah data per halaman" })),
         active: t.Optional(t.String({ description: "Filter aktif (true/false)" })),
      }),
      detail: { summary: "List Desa", tags: ["village"] },
   });

export const GET = AiServer.handle;
export const OPTIONS = AiServer.handle;
