import { prisma } from '@/db';
import { DB_TABLE, LOG_ACTION } 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 { createAuditLog } from './repository';
import {
  baseUser,
  userBanSchema,
  userCreateBESchema,
  userForSelectSchema,
  userListSchema,
  userSetPasswordSchema,
  userUpdateInfoSchema,
  userUpdateRoleBESchema,
} from './user.schema';

export const getAllUser = createServerFn({ method: 'GET' })
  .middleware([authMiddleware])
  .validator(userListSchema)
  .handler(async ({ data }) => {
    try {
      const headers = getRequestHeaders();
      const { page, limit, keyword } = data;

      const list = await auth.api.listUsers({
        query: {
          searchValue: keyword,
          searchField: 'name',
          searchOperator: 'contains',
          sortBy: 'createdAt',
          sortDirection: 'asc',
          limit,
          offset: (page - 1) * limit,
        },
        headers,
      });

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

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

export const getUserForSelect = createServerFn({ method: 'GET' })
  .middleware([authMiddleware])
  .validator(userForSelectSchema)
  .handler(async ({ data, context: { user } }) => {
    try {
      const { keyword, noself } = data;

      const result = await prisma.user.findMany({
        where: {
          OR: [
            {
              name: {
                contains: keyword,
                mode: 'insensitive',
              },
            },
            {
              email: {
                contains: keyword,
                mode: 'insensitive',
              },
            },
          ],
          AND: {
            NOT: {
              id: noself ? user.id : undefined,
            },
          },
        },
        select: {
          id: true,
          name: true,
          email: true,
          image: true,
          role: true,
        },
        orderBy: { createdAt: 'desc' },
        take: 5,
      });

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

export const setUserPassword = createServerFn({ method: 'POST' })
  .middleware([authMiddleware])
  .validator(userSetPasswordSchema)
  .handler(async ({ data, context: { user } }) => {
    try {
      const headers = getRequestHeaders();
      const result = await auth.api.setUserPassword({
        body: {
          newPassword: data.password,
          userId: data.id,
        },
        headers,
      });

      await createAuditLog({
        action: LOG_ACTION.CHANGE_PASSWORD,
        tableName: DB_TABLE.ACCOUNT,
        recordId: data.id,
        oldValue: 'Admin Set User Password',
        newValue: 'Admin Set User Password',
        userId: user.id,
      });

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

export const updateUserInformation = createServerFn({ method: 'POST' })
  .middleware([authMiddleware])
  .validator(userUpdateInfoSchema)
  .handler(async ({ data, context: { user } }) => {
    try {
      const currentUser = await prisma.user.findUnique({
        where: { id: data.id },
        select: { name: true },
      });

      if (!currentUser) throw Error('User not found');

      const headers = getRequestHeaders();
      const result = await auth.api.adminUpdateUser({
        body: {
          userId: data.id, // required
          data: { name: data.name }, // required
        },
        // This endpoint requires session cookies.
        headers,
      });

      await createAuditLog({
        action: LOG_ACTION.UPDATE,
        tableName: DB_TABLE.USER,
        recordId: data.id,
        oldValue: JSON.stringify({ name: currentUser.name }),
        newValue: JSON.stringify({ name: data.name }),
        userId: user.id,
      });

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

export const setUserRole = createServerFn({ method: 'POST' })
  .middleware([authMiddleware])
  .validator(userUpdateRoleBESchema)
  .handler(async ({ data, context: { user } }) => {
    try {
      const currentUser = await prisma.user.findUnique({
        where: { id: data.id },
        select: { role: true },
      });

      if (!currentUser) throw Error('User not found');

      const headers = getRequestHeaders();
      const result = await auth.api.setRole({
        body: {
          userId: data.id,
          role: data.role, // required
        },
        // This endpoint requires session cookies.
        headers,
      });

      await createAuditLog({
        action: LOG_ACTION.UPDATE,
        tableName: DB_TABLE.USER,
        recordId: data.id,
        oldValue: JSON.stringify({ role: currentUser.role }),
        newValue: JSON.stringify({ role: data.role }),
        userId: user.id,
      });

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

export const banUser = createServerFn({ method: 'POST' })
  .middleware([authMiddleware])
  .validator(userBanSchema)
  .handler(async ({ data, context: { user } }) => {
    try {
      const headers = getRequestHeaders();
      const result = await auth.api.banUser({
        body: JSON.parse(
          JSON.stringify({
            userId: data.id, // required
            banReason: data.banReason,
            banExpiresIn:
              data.banExp === 99999 ? undefined : 60 * 60 * 24 * data.banExp,
          }),
        ),
        // This endpoint requires session cookies.
        headers,
      });

      await createAuditLog({
        action: LOG_ACTION.BAN,
        tableName: DB_TABLE.USER,
        recordId: data.id,
        oldValue: null,
        newValue: `Lý do: ${data.banReason} - ${data.banExp} days`,
        userId: user.id,
      });

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

export const unbanUser = createServerFn({ method: 'POST' })
  .middleware([authMiddleware])
  .validator(baseUser)
  .handler(async ({ data, context: { user } }) => {
    try {
      const headers = getRequestHeaders();
      const result = await auth.api.unbanUser({
        body: {
          userId: data.id, // required
        },
        // This endpoint requires session cookies.
        headers,
      });

      await createAuditLog({
        action: LOG_ACTION.UNBAN,
        tableName: DB_TABLE.USER,
        recordId: data.id,
        oldValue: null,
        newValue: null,
        userId: user.id,
      });

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

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

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

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