import { prisma } from '@/db';
import { parseError } from '@/lib/errors';
import { authMiddleware } from '@lib/middleware';
import { createServerFn } from '@tanstack/react-start';
import { extractDiffObjects } from '@utils/helper';
import { createAuditLog, getAllAdminSettings } from './repository';
import { settingSchema, userSettingSchema } from './setting.schema';

// Settings for admin
export const getAdminSettings = createServerFn({ method: 'GET' })
  .middleware([authMiddleware])
  .handler(async () => {
    return await getAllAdminSettings();
  });

export const getCurrentUserLanguage = createServerFn({ method: 'GET' })
  .middleware([authMiddleware])
  .handler(async ({ context }) => {
    try {
      const setting = await prisma.setting.findUniqueOrThrow({
        where: { key: context.user.id, relation: 'user' },
        select: { value: true },
      });

      const value = JSON.parse(setting.value) as UserSetting;

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

export const updateAdminSettings = createServerFn({ method: 'POST' })
  .validator(settingSchema)
  .middleware([authMiddleware])
  .handler(async ({ data, context }) => {
    try {
      const oldSetting = await getAllAdminSettings(true);

      // Update each setting
      const updates = Object.entries(data).map(([key, value]) =>
        prisma.setting.upsert({
          where: { key },
          update: { value },
          create: {
            key,
            value,
            description: key, // or provide proper descriptions
            relation: 'admin',
          },
        }),
      );

      const updated = await prisma.$transaction(updates);

      const [oldValue, newValue] = extractDiffObjects(oldSetting, data);
      const keyEdit = Object.keys(oldValue);
      const listId = updated
        .filter((s) => keyEdit.includes(s.key))
        .map((s) => s.id)
        .join(', ');

      await createAuditLog({
        action: 'update',
        tableName: 'setting',
        recordId: listId,
        oldValue: JSON.stringify(oldValue),
        newValue: JSON.stringify(newValue),
        userId: context.user.id,
      });

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

// Setting for user
type UserSetting = {
  language: string;
};

export const getUserSettings = createServerFn({ method: 'GET' })
  .middleware([authMiddleware])
  .handler(async ({ context }) => {
    try {
      const settings = await prisma.setting.findUniqueOrThrow({
        where: {
          relation: 'user',
          key: context.user.id,
        },
        select: {
          id: true,
          key: true,
          value: true,
        },
      });

      return {
        settings,
        value: JSON.parse(settings.value) as UserSetting,
      };
    } catch (error) {
      console.error(error);
      const { message, code } = parseError(error);
      throw { message, code };
    }
  });

export const updateUserSettings = createServerFn({ method: 'POST' })
  .validator(userSettingSchema)
  .middleware([authMiddleware])
  .handler(async ({ data, context }) => {
    // Update each setting
    try {
      const oldSetting = await prisma.setting.findUnique({
        where: {
          relation: 'user',
          key: context.user.id,
        },
        select: {
          value: true,
        },
      });

      const updated = await prisma.setting.upsert({
        where: { key: context.user.id },
        update: { value: JSON.stringify(data) },
        create: {
          key: context.user.id,
          value: JSON.stringify(data),
          description: 'User settings', // or provide proper descriptions
          relation: 'user',
        },
      });

      if (oldSetting) {
        await createAuditLog({
          action: 'update',
          tableName: 'setting',
          recordId: updated.id,
          oldValue: oldSetting.value,
          newValue: JSON.stringify(data),
          userId: context.user.id,
        });
      }

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