import { prisma } from "@/lib/prisma";
import { normalizeAppRole } from "@/lib/roles";
import type { AuthUserUpdateInput } from "@/validators/auth-users";
import type { AuthUserPermissionsUpdateInput } from "@/validators/auth-users";

const authUserSelect = {
  id: true,
  username: true,
  email: true,
  role: true,
  is_active: true,
  provider: true,
  created_at: true,
  updated_at: true,
  last_login_at: true,
  all_sectors: true,
  all_establishments: true,
  all_categories: true,
  all_contract_types: true,
  permissions: {
    orderBy: { module: "asc" as const },
    select: {
      module: true,
      can_read: true,
      can_write: true,
    },
  },
  sectors: {
    orderBy: { sector_id: "asc" as const },
    select: {
      sector: {
        select: {
          id: true,
          secteurs: true,
        },
      },
    },
  },
  establishments: {
    orderBy: { establishment_id: "asc" as const },
    select: {
      establishment: {
        select: {
          id: true,
          etb: true,
          entreprise: true,
        },
      },
    },
  },
  categories: {
    orderBy: { category: "asc" as const },
    select: {
      category: true,
    },
  },
  contract_types: {
    orderBy: { contract_type_id: "asc" as const },
    select: {
      contract_type: {
        select: {
          id: true,
          tcs: true,
        },
      },
    },
  },
} as const;

function toPublicAuthUser<T extends { role: string }>(user: T) {
  return {
    ...user,
    role: normalizeAppRole(user.role) ?? user.role,
  };
}

export async function listPermissionSectors() {
  return prisma.secteurs_travail.findMany({
    where: {
      actif: true,
      secteurs: { not: null },
    },
    orderBy: { secteurs: "asc" },
    select: {
      id: true,
      secteurs: true,
    },
  });
}

export async function listPermissionEstablishments() {
  return prisma.etablissement.findMany({
    where: {
      actif: true,
      etb: { not: null },
    },
    orderBy: [{ entreprise: "asc" }, { etb: "asc" }],
    select: {
      id: true,
      etb: true,
      entreprise: true,
    },
  });
}

export async function listPermissionCategories() {
  const rows = await prisma.contrats.findMany({
    where: { CAT: { not: null } },
    distinct: ["CAT"],
    orderBy: { CAT: "asc" },
    select: { CAT: true },
  });

  return [
    ...new Set(rows.map((row) => row.CAT?.trim()).filter(Boolean)),
  ] as string[];
}

export async function listPermissionContractTypes() {
  return prisma.types_contrat_salarie.findMany({
    where: {
      actif: true,
      tcs: { not: null },
    },
    orderBy: { tcs: "asc" },
    select: {
      id: true,
      tcs: true,
    },
  });
}

export async function listAuthUsers(search = "") {
  const normalizedSearch = search.trim();

  const users = await prisma.auth_users.findMany({
    where: normalizedSearch
      ? {
        OR: [
          {
            username: {
              contains: normalizedSearch,
            },
          },
          {
            email: {
              contains: normalizedSearch,
            },
          },
        ],
      }
      : undefined,
    orderBy: [
      {
        username: "asc",
      },
    ],
    select: authUserSelect,
  });

  return users.map(toPublicAuthUser);
}

export async function getAuthUserById(id: number) {
  const user = await prisma.auth_users.findUnique({
    where: {
      id,
    },
    select: authUserSelect,
  });

  return user ? toPublicAuthUser(user) : null;
}

export async function updateAuthUserById(
  id: number,
  data: AuthUserUpdateInput,
) {
  const user = await prisma.auth_users.update({
    where: {
      id,
    },
    data,
    select: authUserSelect,
  });

  return toPublicAuthUser(user);
}

export async function replaceAuthUserPermissions(
  id: number,
  data: AuthUserPermissionsUpdateInput,
) {
  const uniqueSectorIds = [...new Set(data.sectorIds)];
  const uniqueEstablishmentIds = [...new Set(data.establishmentIds)];
  const uniqueCategories = [
    ...new Set(data.categories.map((item) => item.trim())),
  ];
  const uniqueContractTypeIds = [...new Set(data.contractTypeIds)];

  return prisma.$transaction(async (transaction) => {
    const target = await transaction.auth_users.findUnique({
      where: { id },
      select: { id: true, role: true },
    });

    if (!target) return null;

    if (target.role === "super_admin") {
      throw new Error("SUPER_ADMIN_PROTECTED");
    }

    const existingSectorCount = uniqueSectorIds.length
      ? await transaction.secteurs_travail.count({
        where: { id: { in: uniqueSectorIds } },
      })
      : 0;

    if (existingSectorCount !== uniqueSectorIds.length) {
      throw new Error("UNKNOWN_SECTOR");
    }

    const existingEstablishmentCount = uniqueEstablishmentIds.length
      ? await transaction.etablissement.count({
        where: { id: { in: uniqueEstablishmentIds }, actif: true },
      })
      : 0;

    if (existingEstablishmentCount !== uniqueEstablishmentIds.length) {
      throw new Error("UNKNOWN_ESTABLISHMENT");
    }

    const knownCategories = uniqueCategories.length
      ? await transaction.contrats.findMany({
        where: { CAT: { not: null } },
        distinct: ["CAT"],
        select: { CAT: true },
      })
      : [];
    const knownCategoryNames = new Set(
      knownCategories.map((item) => item.CAT?.trim()).filter(Boolean),
    );

    if (
      uniqueCategories.some((category) => !knownCategoryNames.has(category))
    ) {
      throw new Error("UNKNOWN_CATEGORY");
    }

    const existingContractTypeCount = uniqueContractTypeIds.length
      ? await transaction.types_contrat_salarie.count({
        where: { id: { in: uniqueContractTypeIds }, actif: true },
      })
      : 0;

    if (existingContractTypeCount !== uniqueContractTypeIds.length) {
      throw new Error("UNKNOWN_CONTRACT_TYPE");
    }

    await transaction.auth_users.update({
      where: { id },
      data: {
        all_sectors: data.allSectors,
        all_establishments: data.allEstablishments,
        all_categories: data.allCategories,
        all_contract_types: data.allContractTypes,
      },
    });

    await transaction.auth_user_permissions.deleteMany({
      where: { user_id: id },
    });
    await transaction.auth_user_sectors.deleteMany({
      where: { user_id: id },
    });
    await transaction.auth_user_establishments.deleteMany({
      where: { user_id: id },
    });
    await transaction.auth_user_categories.deleteMany({
      where: { user_id: id },
    });
    await transaction.auth_user_contract_types.deleteMany({
      where: { user_id: id },
    });

    await transaction.auth_user_permissions.createMany({
      data: data.permissions.map((permission) => ({
        user_id: id,
        module: permission.module,
        can_read: permission.canRead,
        can_write: permission.canWrite,
      })),
    });

    if (!data.allSectors && uniqueSectorIds.length) {
      await transaction.auth_user_sectors.createMany({
        data: uniqueSectorIds.map((sectorId) => ({
          user_id: id,
          sector_id: sectorId,
        })),
      });
    }

    if (!data.allEstablishments && uniqueEstablishmentIds.length) {
      await transaction.auth_user_establishments.createMany({
        data: uniqueEstablishmentIds.map((establishmentId) => ({
          user_id: id,
          establishment_id: establishmentId,
        })),
      });
    }

    if (!data.allCategories && uniqueCategories.length) {
      await transaction.auth_user_categories.createMany({
        data: uniqueCategories.map((category) => ({
          user_id: id,
          category,
        })),
        skipDuplicates: true,
      });
    }

    if (!data.allContractTypes && uniqueContractTypeIds.length) {
      await transaction.auth_user_contract_types.createMany({
        data: uniqueContractTypeIds.map((contractTypeId) => ({
          user_id: id,
          contract_type_id: contractTypeId,
        })),
      });
    }

    return transaction.auth_users.findUnique({
      where: { id },
      select: authUserSelect,
    });
  });
}
