import { badRequest, forbidden, unauthorized } from "@infra/errors";
import { asyncRoute, fail, ok } from "@infra/http";
import { logger } from "@infra/logger";
import {
  isBasicAuthEnabled,
  parseBasicAuthUsername,
} from "@server/infra/basic-auth-credentials";
import * as profilesRepo from "@server/repositories/profiles";
import { setSetting } from "@server/repositories/settings";
import { clearProfileCache, getProfile } from "@server/services/profile";
import { generateProfileFromResume } from "@server/services/profile-generator";
import { jobSearchProfileSchema } from "@shared/settings-registry";
import type { SearchProfile } from "@shared/types";
import { type Request, type Response, Router } from "express";

export const profilesRouter = Router();

function assertProfileVisibleToRequest(
  req: Request,
  profile: SearchProfile,
): true | "unauthorized" | "forbidden" {
  if (!isBasicAuthEnabled()) return true;
  const username = parseBasicAuthUsername(req.headers.authorization);
  if (!username?.trim()) return "unauthorized";
  return profilesRepo.profileMatchesBasicAuthUser(
    profile.data.basicAuthUser,
    username,
  )
    ? true
    : "forbidden";
}

profilesRouter.get(
  "/",
  asyncRoute(async (req: Request, res: Response) => {
    const username = parseBasicAuthUsername(req.headers.authorization);
    const profiles =
      isBasicAuthEnabled() && username?.trim()
        ? await profilesRepo.listProfilesForBasicAuthUser(username)
        : await profilesRepo.listProfiles();
    return ok(res, profiles);
  }),
);

profilesRouter.get(
  "/:id",
  asyncRoute(async (req: Request, res: Response) => {
    const profile = await profilesRepo.getProfileById(req.params.id);
    if (!profile) {
      return fail(res, badRequest("Profile not found"));
    }
    const visible = assertProfileVisibleToRequest(req, profile);
    if (visible === "unauthorized") {
      return fail(res, unauthorized("Authentication required"));
    }
    if (visible === "forbidden") {
      return fail(res, forbidden("You cannot access this profile"));
    }
    return ok(res, profile);
  }),
);

profilesRouter.post(
  "/",
  asyncRoute(async (req: Request, res: Response) => {
    const { name, data } = req.body;
    if (!name || typeof name !== "string" || name.trim().length === 0) {
      throw badRequest("Profile name is required");
    }
    const parsed = jobSearchProfileSchema.safeParse(data);
    if (!parsed.success) {
      throw badRequest("Invalid profile data", {
        issues: parsed.error.issues,
      });
    }
    const username = parseBasicAuthUsername(req.headers.authorization)?.trim();
    const dataWithOwner =
      isBasicAuthEnabled() && username
        ? { ...parsed.data, basicAuthUser: username }
        : parsed.data;
    const profile = await profilesRepo.createProfile({
      name: name.trim(),
      data: dataWithOwner,
    });
    return ok(res, profile);
  }),
);

profilesRouter.patch(
  "/:id",
  asyncRoute(async (req: Request, res: Response) => {
    const existing = await profilesRepo.getProfileById(req.params.id);
    if (!existing) {
      return fail(res, badRequest("Profile not found"));
    }
    const visible = assertProfileVisibleToRequest(req, existing);
    if (visible === "unauthorized") {
      return fail(res, unauthorized("Authentication required"));
    }
    if (visible === "forbidden") {
      return fail(res, forbidden("You cannot update this profile"));
    }
    const { name, data } = req.body;
    const updates: { name?: string; data?: typeof data } = {};
    if (name !== undefined) {
      if (typeof name !== "string" || name.trim().length === 0) {
        throw badRequest("Profile name must be a non-empty string");
      }
      updates.name = name.trim();
    }
    if (data !== undefined) {
      const parsed = jobSearchProfileSchema.safeParse(data);
      if (!parsed.success) {
        throw badRequest("Invalid profile data", {
          issues: parsed.error.issues,
        });
      }
      const username = parseBasicAuthUsername(
        req.headers.authorization,
      )?.trim();
      if (isBasicAuthEnabled() && username) {
        const locked = (existing.data.basicAuthUser ?? "").trim();
        if (locked && locked.toLowerCase() !== username.toLowerCase()) {
          return fail(res, forbidden("Cannot reassign basicAuthUser"));
        }
        updates.data = { ...parsed.data, basicAuthUser: username };
      } else {
        updates.data = parsed.data;
      }
    }
    const profile = await profilesRepo.updateProfile(req.params.id, updates);
    if (!profile) {
      return fail(res, badRequest("Profile not found"));
    }
    return ok(res, profile);
  }),
);

profilesRouter.delete(
  "/:id",
  asyncRoute(async (req: Request, res: Response) => {
    const existing = await profilesRepo.getProfileById(req.params.id);
    if (!existing) {
      return fail(res, badRequest("Profile not found"));
    }
    const visible = assertProfileVisibleToRequest(req, existing);
    if (visible === "unauthorized") {
      return fail(res, unauthorized("Authentication required"));
    }
    if (visible === "forbidden") {
      return fail(res, forbidden("You cannot delete this profile"));
    }
    const deleted = await profilesRepo.deleteProfile(req.params.id);
    if (!deleted) {
      return fail(res, badRequest("Profile not found"));
    }
    return ok(res, { deleted: true });
  }),
);

profilesRouter.post(
  "/:id/activate",
  asyncRoute(async (req: Request, res: Response) => {
    const profile = await profilesRepo.getProfileById(req.params.id);
    if (!profile) {
      return fail(res, badRequest("Profile not found"));
    }
    const visible = assertProfileVisibleToRequest(req, profile);
    if (visible === "unauthorized") {
      return fail(res, unauthorized("Authentication required"));
    }
    if (visible === "forbidden") {
      return fail(res, forbidden("You cannot activate this profile"));
    }
    const basicUser = parseBasicAuthUsername(req.headers.authorization)?.trim();

    if (isBasicAuthEnabled() && basicUser) {
      await profilesRepo.setActiveSearchProfileIdForBasicAuthUser(
        basicUser,
        profile.id,
      );
      clearProfileCache();
      return ok(res, { activated: true, profileId: profile.id });
    }

    const resumePath = profile.data.resumeLocalPath?.trim();
    await setSetting("activeProfileId", profile.id);
    await setSetting("jobSearchProfile", JSON.stringify(profile.data));
    if (resumePath) {
      await setSetting("localResumeProfilePath", resumePath);
      clearProfileCache();
    }
    return ok(res, { activated: true, profileId: profile.id });
  }),
);

profilesRouter.post(
  "/generate-from-resume",
  asyncRoute(async (_req: Request, res: Response) => {
    logger.info("Generating profile from resume");
    const resumeProfile = await getProfile();
    if (
      !resumeProfile ||
      typeof resumeProfile !== "object" ||
      Array.isArray(resumeProfile)
    ) {
      throw badRequest(
        "No resume profile available. Configure a local resume JSON path in Settings first.",
      );
    }
    const generated = await generateProfileFromResume(
      resumeProfile as Record<string, unknown>,
    );
    return ok(res, generated);
  }),
);
