import { prisma } from '@/db';
import { HouseWhereInput } from '@/generated/prisma/models';
import { DB_TABLE, LOG_ACTION, NOTIFICATION_TYPE } from '@/types/enum';
import { auth } from '@lib/auth';
import { parseError } from '@lib/errors';
import { authMiddleware } from '@lib/middleware';
import { createServerFn } from '@tanstack/react-start';
import { getRequestHeaders } from '@tanstack/react-start/server';
import {
  actionInvitationSchema,
  baseHouse,
  houseCreateBESchema,
  houseEditBESchema,
  houseForSelectSchema,
  houseListSchema,
  invitationCreateBESchema,
  removeMemberSchema,
} from './house.schema';
import { createAuditLog, createNotification } from './repository';

export const getAllHouse = createServerFn({ method: 'GET' })
  .middleware([authMiddleware])
  .validator(houseListSchema)
  .handler(async ({ data }) => {
    try {
      const { page, limit, keyword } = data;
      const skip = (page - 1) * limit;

      const where: HouseWhereInput = {
        OR: [
          {
            name: {
              contains: keyword,
              mode: 'insensitive',
            },
          },
        ],
      };

      const [list, total]: [HouseWithMembers[], number] = await Promise.all([
        await prisma.house.findMany({
          where,
          orderBy: { createdAt: 'desc' },
          include: {
            members: {
              select: {
                role: true,
                user: {
                  select: {
                    id: true,
                    name: true,
                    email: true,
                    image: true,
                  },
                },
              },
            },
          },
          take: limit,
          skip,
        }),
        await prisma.house.count({ where }),
      ]);

      const totalPage = Math.ceil(+total / limit);

      return {
        result: list,
        pagination: {
          currentPage: page,
          totalPage,
          totalItem: total,
        },
      };
    } catch (error) {
      console.error(error);
      const { message, code } = parseError(error);
      throw { message, code };
    }
  });

export const getCurrentUserHouses = createServerFn({ method: 'GET' })
  .middleware([authMiddleware])
  .handler(async ({ context: { user } }) => {
    try {
      const houses = await prisma.house.findMany({
        where: { members: { some: { userId: user.id } } },
        orderBy: { createdAt: 'asc' },
        include: {
          _count: {
            select: {
              members: true,
            },
          },
          members: {
            select: {
              role: true,
              user: {
                select: {
                  id: true,
                  name: true,
                  email: true,
                  image: true,
                },
              },
            },
          },
        },
      });

      return houses;
    } catch (error) {
      console.error(error);
      const { message, code } = parseError(error);
      throw { message, code };
    }
  });

export const getHouseForSelect = createServerFn({ method: 'GET' })
  .middleware([authMiddleware])
  .validator(houseForSelectSchema)
  .handler(async ({ data }) => {
    try {
      const result = await prisma.house.findMany({
        where: {
          OR: [
            {
              name: {
                contains: data.keyword,
                mode: 'insensitive',
              },
            },
          ],
        },
        select: {
          id: true,
          name: true,
          color: true,
        },
        orderBy: { createdAt: 'desc' },
        take: 5,
      });

      return result;
    } catch (error) {
      console.error(error);
      const { message, code } = parseError(error);
      throw { message, code };
    }
  });

export const createHouse = createServerFn({ method: 'POST' })
  .middleware([authMiddleware])
  .validator(houseCreateBESchema)
  .handler(async ({ data, context: { user } }) => {
    try {
      const result = await auth.api.createOrganization({
        body: data,
      });

      if (!result) throw Error('Failed to create house');

      await createAuditLog({
        action: LOG_ACTION.CREATE,
        tableName: DB_TABLE.ORGANIZATION,
        recordId: result.id,
        oldValue: '',
        newValue: JSON.stringify(result),
        userId: user.id,
      });

      return result;
    } catch (error) {
      console.error(error);
      const { message, code } = parseError(error);
      throw { message, code };
    }
  });

export const updateHouse = createServerFn({ method: 'POST' })
  .middleware([authMiddleware])
  .validator(houseEditBESchema)
  .handler(async ({ data, context: { user } }) => {
    try {
      const currentHouse = await prisma.house.findUnique({
        where: { id: data.id },
      });
      if (!currentHouse) throw Error('House not found');

      const { id, slug, name, color } = data;
      const result = await prisma.house.update({
        where: { id },
        data: {
          name,
          slug,
          color,
        },
      });

      await createAuditLog({
        action: LOG_ACTION.UPDATE,
        tableName: DB_TABLE.ORGANIZATION,
        recordId: id,
        oldValue: JSON.stringify(currentHouse),
        newValue: JSON.stringify(result),
        userId: user.id,
      });

      return result;
    } catch (error) {
      console.error(error);
      const { message, code } = parseError(error);
      throw { message, code };
    }
  });

export const deleteHouse = createServerFn({ method: 'POST' })
  .middleware([authMiddleware])
  .validator(baseHouse)
  .handler(async ({ data, context: { user } }) => {
    try {
      const currentHouse = await prisma.house.findUnique({
        where: { id: data.id },
      });
      if (!currentHouse) throw Error('House not found');

      const result = await Promise.all([
        prisma.house.delete({
          where: { id: data.id },
        }),
        prisma.member.deleteMany({
          where: { organizationId: data.id },
        }),
        prisma.invitation.deleteMany({
          where: { organizationId: data.id },
        }),
      ]);

      await createAuditLog({
        action: LOG_ACTION.DELETE,
        tableName: DB_TABLE.ORGANIZATION,
        recordId: result[0]?.id,
        oldValue: JSON.stringify(currentHouse),
        newValue: '',
        userId: user.id,
      });

      return result;
    } catch (error) {
      console.error(error);
      const { message, code } = parseError(error);
      throw { message, code };
    }
  });

export const deleteUserHouse = createServerFn({ method: 'POST' })
  .middleware([authMiddleware])
  .validator(baseHouse)
  .handler(async ({ data, context: { user } }) => {
    try {
      const currentHouse = await prisma.house.findUnique({
        where: { id: data.id },
      });
      if (!currentHouse) throw Error('House not found');

      const headers = getRequestHeaders();
      const result = await auth.api.deleteOrganization({
        body: {
          organizationId: data.id, // required
        },
        headers,
      });

      if (result) {
        await createAuditLog({
          action: LOG_ACTION.DELETE,
          tableName: DB_TABLE.ORGANIZATION,
          recordId: result?.id,
          oldValue: JSON.stringify(currentHouse),
          newValue: '',
          userId: user.id,
        });
      }

      return result;
    } catch (error) {
      console.error(error);
      const { message, code } = parseError(error);
      throw { message, code };
    }
  });

export const invitationMember = createServerFn({ method: 'POST' })
  .middleware([authMiddleware])
  .validator(invitationCreateBESchema)
  .handler(async ({ data, context: { user } }) => {
    try {
      const headers = getRequestHeaders();
      const body = {
        email: data.email,
        role: data.role,
        organizationId: data.houseId,
      };

      const cuser = await prisma.user.findUnique({
        where: { email: data.email },
      });
      const chouse = await prisma.house.findUnique({
        where: { id: data.houseId },
      });

      const result = await auth.api.createInvitation({
        body,
        headers,
      });

      if (result && cuser && chouse) {
        await createNotification({
          type: NOTIFICATION_TYPE.INVITATION,
          userId: cuser.id,
          title: 'INVITATION_HOUSE',
          message: 'INVITATION_HOUSE',
          link: result.id,
          metadata: JSON.stringify({
            house: chouse,
          }),
          readAt: null,
        });

        await createAuditLog({
          action: LOG_ACTION.CREATE,
          tableName: DB_TABLE.INVITATION,
          recordId: result.id,
          oldValue: '',
          newValue: JSON.stringify(body),
          userId: user.id,
        });
      }

      return result;
    } catch (error) {
      console.error(error);
      const { message, code } = parseError(error);
      throw { message, code };
    }
  });

export const cancelInvitation = createServerFn({ method: 'POST' })
  .middleware([authMiddleware])
  .validator(baseHouse)
  .handler(async ({ data }) => {
    try {
      const headers = getRequestHeaders();
      const result = await auth.api.cancelInvitation({
        body: {
          invitationId: data.id, // required
        },
        headers,
      });

      if (result) {
        const notification = await prisma.notification.findFirst({
          where: { link: data.id },
        });

        if (notification) {
          await prisma.notification.update({
            where: { id: notification.id },
            data: { link: null },
          });
        }
      }

      return result;
    } catch (error) {
      console.error(error);
      const { message, code } = parseError(error);
      throw { message, code };
    }
  });

export const acceptInvitation = createServerFn({ method: 'POST' })
  .middleware([authMiddleware])
  .validator(actionInvitationSchema)
  .handler(async ({ data, context: { user } }) => {
    try {
      const result = await prisma.invitation.update({
        where: { id: data.id },
        data: { status: 'accepted' },
      });

      if (result) {
        const notify = await prisma.notification.update({
          where: { id: data.notificationId },
          data: { link: null },
        });

        const member = await auth.api.addMember({
          body: {
            userId: notify.userId,
            organizationId: result.organizationId,
            role: (result.role as 'admin' | 'owner' | 'member') || 'member',
          },
        });

        await createAuditLog({
          action: LOG_ACTION.UPDATE,
          tableName: DB_TABLE.INVITATION,
          recordId: result.id,
          oldValue: JSON.stringify(result),
          newValue: 'Accept Invitation / Đồng ý mời',
          userId: user.id,
        });

        if (member) {
          await createAuditLog({
            action: LOG_ACTION.CREATE,
            tableName: DB_TABLE.MEMBER,
            recordId: member.id,
            oldValue: '',
            newValue: JSON.stringify(member),
            userId: user.id,
          });
        }
      }

      return result;
    } catch (error) {
      console.error(error);
      const { message, code } = parseError(error);
      throw { message, code };
    }
  });

export const rejectInvitation = createServerFn({ method: 'POST' })
  .middleware([authMiddleware])
  .validator(actionInvitationSchema)
  .handler(async ({ data }) => {
    try {
      const headers = getRequestHeaders();
      const result = await auth.api.rejectInvitation({
        body: {
          invitationId: data.id,
        },
        headers,
      });

      if (result) {
        await prisma.notification.update({
          where: { id: data.notificationId },
          data: { link: null },
        });
      }

      return result;
    } catch (error) {
      console.error(error);
      const { message, code } = parseError(error);
      throw { message, code };
    }
  });

export const leaveHouse = createServerFn({ method: 'POST' })
  .middleware([authMiddleware])
  .validator(baseHouse)
  .handler(async ({ data }) => {
    try {
      const headers = getRequestHeaders();
      const result = await auth.api.leaveOrganization({
        body: {
          organizationId: data.id,
        },
        headers,
      });

      if (result) {
        await createAuditLog({
          action: LOG_ACTION.UPDATE,
          tableName: DB_TABLE.ORGANIZATION,
          recordId: result.id,
          oldValue: JSON.stringify(result),
          newValue: `${result.user.name} đã rời khỏi nhà (left the house)`,
          userId: result.user.id,
        });
      }

      return result;
    } catch (error) {
      console.error(error);
      const { message, code } = parseError(error);
      throw { message, code };
    }
  });

export const removeMember = createServerFn({ method: 'POST' })
  .middleware([authMiddleware])
  .validator(removeMemberSchema)
  .handler(async ({ data, context: { user } }) => {
    try {
      const headers = getRequestHeaders();
      const result = await auth.api.removeMember({
        body: {
          memberIdOrEmail: data.memberId,
          organizationId: data.houseId,
        },
        headers,
      });

      if (result) {
        const oldMember = await prisma.user.findUnique({
          where: { id: result.member.userId },
        });

        await createAuditLog({
          action: LOG_ACTION.UPDATE,
          tableName: DB_TABLE.MEMBER,
          recordId: result.member.id,
          oldValue: JSON.stringify(result),
          newValue: `${oldMember?.name} đã bị "mời" rời khỏi nhà (was "asked" to leave the house.)`,
          userId: user.id,
        });
      }

      return result;
    } catch (error) {
      console.error(error);
      const { message, code } = parseError(error);
      throw { message, code };
    }
  });
