import { randomUUID } from "node:crypto";
import type {
  CreateSearchProfileInput,
  JobSearchProfile,
  SearchProfile,
  UpdateSearchProfileInput,
} from "@shared/types";
import { eq } from "drizzle-orm";
import { db } from "../db";
import { searchProfiles } from "../db/schema";
import type { SettingKey } from "./settings";
import { getSetting, setSetting } from "./settings";

const BASIC_AUTH_ACTIVE_PROFILE_BY_USER_KEY: SettingKey =
  "basicAuthActiveProfileByUser";

type BasicAuthActiveProfileByUser = Record<string, string>;

function mapRow(row: typeof searchProfiles.$inferSelect): SearchProfile {
  let data: JobSearchProfile;
  try {
    data = JSON.parse(row.data);
  } catch {
    data = {
      targetRoles: [],
      experienceLevel: "",
      mustHaveSkills: [],
      niceToHaveSkills: [],
      dealBreakers: [],
      preferredWorkArrangement: [],
      preferredLocations: [],
      minimumSalary: "",
      industriesToTarget: [],
      industriesToAvoid: [],
      aboutMe: "",
    };
  }
  return {
    id: row.id,
    name: row.name,
    data,
    createdAt: row.createdAt,
    updatedAt: row.updatedAt,
  };
}

export async function listProfiles(): Promise<SearchProfile[]> {
  const rows = await db.select().from(searchProfiles);
  return rows.map(mapRow);
}

export function profileMatchesBasicAuthUser(
  basicAuthUser: string | null | undefined,
  username: string,
): boolean {
  const u = username.trim().toLowerCase();
  if (!u) return false;
  const aliases = (basicAuthUser ?? "")
    .split(",")
    .map((part) => part.trim().toLowerCase())
    .filter(Boolean);
  return aliases.includes(u);
}

/** When Basic Auth maps users to profiles, each user only sees rows with matching `basicAuthUser`. */
export async function listProfilesForBasicAuthUser(
  username: string,
): Promise<SearchProfile[]> {
  const all = await listProfiles();
  return all.filter((p) =>
    profileMatchesBasicAuthUser(p.data.basicAuthUser, username),
  );
}

export async function getProfileById(
  id: string,
): Promise<SearchProfile | null> {
  const [row] = await db
    .select()
    .from(searchProfiles)
    .where(eq(searchProfiles.id, id));
  return row ? mapRow(row) : null;
}

export async function createProfile(
  input: CreateSearchProfileInput,
): Promise<SearchProfile> {
  const id = randomUUID();
  const now = new Date().toISOString();
  await db.insert(searchProfiles).values({
    id,
    name: input.name,
    data: JSON.stringify(input.data),
    createdAt: now,
    updatedAt: now,
  });
  const created = await getProfileById(id);
  if (!created) throw new Error("Failed to create profile");
  return created;
}

export async function updateProfile(
  id: string,
  input: UpdateSearchProfileInput,
): Promise<SearchProfile | null> {
  const existing = await getProfileById(id);
  if (!existing) return null;

  const updates: Partial<typeof searchProfiles.$inferInsert> = {
    updatedAt: new Date().toISOString(),
  };
  if (input.name !== undefined) updates.name = input.name;
  if (input.data !== undefined) updates.data = JSON.stringify(input.data);

  await db.update(searchProfiles).set(updates).where(eq(searchProfiles.id, id));

  return getProfileById(id);
}

function parseBasicAuthActiveProfileByUser(
  raw: string | null | undefined,
): BasicAuthActiveProfileByUser {
  if (!raw?.trim()) return {};
  try {
    const parsed: unknown = JSON.parse(raw);
    if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
      return {};
    }
    const out: BasicAuthActiveProfileByUser = {};
    for (const [key, value] of Object.entries(parsed)) {
      const alias = key.trim().toLowerCase();
      const profileId = typeof value === "string" ? value.trim() : "";
      if (alias && profileId) out[alias] = profileId;
    }
    return out;
  } catch {
    return {};
  }
}

async function loadBasicAuthActiveProfileByUser(): Promise<BasicAuthActiveProfileByUser> {
  const raw = await getSetting(BASIC_AUTH_ACTIVE_PROFILE_BY_USER_KEY);
  return parseBasicAuthActiveProfileByUser(raw);
}

function basicAuthAliases(basicAuthUser: string | null | undefined): string[] {
  return (basicAuthUser ?? "")
    .split(",")
    .map((part) => part.trim().toLowerCase())
    .filter(Boolean);
}

/**
 * When one login maps to multiple search profiles, remember which profile is active.
 * Writes the same profile id for every alias on that profile (e.g. ilia + dobkin).
 */
export async function setActiveSearchProfileIdForBasicAuthUser(
  username: string,
  profileId: string,
): Promise<void> {
  const profile = await getProfileById(profileId);
  if (!profile) return;

  const requestAlias = username.trim().toLowerCase();
  if (!requestAlias) return;
  if (!profileMatchesBasicAuthUser(profile.data.basicAuthUser, requestAlias)) {
    return;
  }

  const map = await loadBasicAuthActiveProfileByUser();
  for (const alias of basicAuthAliases(profile.data.basicAuthUser)) {
    map[alias] = profileId;
  }
  await setSetting(BASIC_AUTH_ACTIVE_PROFILE_BY_USER_KEY, JSON.stringify(map));
}

export async function getSearchProfileIdForBasicAuthUser(
  username: string,
): Promise<string | null> {
  const matches = await listProfilesForBasicAuthUser(username);
  if (matches.length === 0) return null;
  if (matches.length === 1) return matches[0].id;

  const map = await loadBasicAuthActiveProfileByUser();
  const preferred = map[username.trim().toLowerCase()];
  if (preferred && matches.some((profile) => profile.id === preferred)) {
    return preferred;
  }
  return matches[0].id;
}

export async function deleteProfile(id: string): Promise<boolean> {
  const result = await db
    .delete(searchProfiles)
    .where(eq(searchProfiles.id, id));
  return (result?.changes ?? 0) > 0;
}
