import { logger } from "@infra/logger";
import { getJobOwnerProfileId } from "@infra/request-context";
import { DEFAULT_JOB_OWNER_PROFILE_ID } from "@server/infra/job-owner-context";
import * as profilesRepo from "@server/repositories/profiles";
import * as settingsRepo from "@server/repositories/settings";
import {
  getDefaultModelForProvider,
  jobSearchProfileSchema,
  settingsRegistry,
} from "@shared/settings-registry";
import type { AppSettings } from "@shared/types";
import { getEnvSettingsData } from "./envSettings";
import { getProfile } from "./profile";
import { resolveResumeProjectsSettings } from "./resumeProjects";
import {
  extractProjectsFromResume,
  getResume,
  RxResumeAuthConfigError,
} from "./rxresume";
import { resolveRxResumeBaseResumeIdForMode } from "./rxresume/baseResumeId";

function resolveDefaultLlmBaseUrl(provider: string): string {
  const normalized = provider.trim().toLowerCase().replace(/-/g, "_");
  if (normalized === "ollama") return "http://localhost:11434";
  if (normalized === "lmstudio") return "http://localhost:1234";
  if (normalized === "openai") {
    return "https://api.openai.com";
  }
  if (normalized === "openai_compatible") {
    return "https://api.openai.com";
  }
  if (normalized === "gemini") {
    return "https://generativelanguage.googleapis.com";
  }
  return "https://openrouter.ai";
}

function normalizeModelForProviderCompatibility(
  provider: string | null | undefined,
  model: string | null | undefined,
): string | null {
  const trimmedModel = model?.trim();
  if (!trimmedModel) return null;

  const normalizedProvider = provider?.trim().toLowerCase().replace(/-/g, "_");
  const normalizedModel = trimmedModel.toLowerCase();

  if (normalizedProvider === "openai") {
    if (
      normalizedModel.startsWith("google/") ||
      normalizedModel.startsWith("models/") ||
      normalizedModel.startsWith("gemini")
    ) {
      return null;
    }
  }

  if (normalizedProvider === "gemini") {
    const isGeminiModel =
      normalizedModel.startsWith("google/") ||
      normalizedModel.startsWith("models/") ||
      normalizedModel.startsWith("gemini");
    if (!isGeminiModel) {
      return null;
    }
  }

  return trimmedModel;
}

/**
 * Get the effective app settings, combining environment variables and database overrides.
 */
export async function getEffectiveSettings(): Promise<AppSettings> {
  const getAllSettings =
    "getAllSettings" in settingsRepo ? settingsRepo.getAllSettings : null;
  const overrides =
    (typeof getAllSettings === "function" ? await getAllSettings() : null) ??
    {};
  const providerOverride = settingsRegistry.llmProvider.parse(
    overrides.llmProvider,
  );
  const effectiveLlmProvider =
    providerOverride ?? settingsRegistry.llmProvider.default();
  const resolvedModelDefault =
    normalizeModelForProviderCompatibility(
      effectiveLlmProvider,
      getDefaultModelForProvider(effectiveLlmProvider, process.env.MODEL),
    ) ?? getDefaultModelForProvider(effectiveLlmProvider);

  const rxresumeBaseResumeId = resolveRxResumeBaseResumeIdForMode({
    rxresumeMode: overrides.rxresumeMode ?? process.env.RXRESUME_MODE ?? null,
    rxresumeBaseResumeId: overrides.rxresumeBaseResumeId ?? null,
    rxresumeBaseResumeIdV4: overrides.rxresumeBaseResumeIdV4 ?? null,
    rxresumeBaseResumeIdV5: overrides.rxresumeBaseResumeIdV5 ?? null,
  });
  let profile: Record<string, unknown> = {};

  try {
    profile = (await getProfile()) as Record<string, unknown>;
  } catch (error) {
    logger.warn("Failed to load base resume profile for settings (primary)", {
      error,
    });
    if (rxresumeBaseResumeId) {
      try {
        const resume = await getResume(rxresumeBaseResumeId);
        if (resume.data && typeof resume.data === "object") {
          profile = resume.data as Record<string, unknown>;
        }
      } catch (rxError) {
        if (rxError instanceof RxResumeAuthConfigError) {
          logger.warn(
            "Reactive Resume credentials missing during settings load",
            {
              resumeId: rxresumeBaseResumeId,
              error: rxError,
            },
          );
        } else {
          logger.warn(
            "Failed to load Reactive Resume base resume for settings",
            {
              resumeId: rxresumeBaseResumeId,
              error: rxError,
            },
          );
        }
      }
    }
  }

  const envSettings = await getEnvSettingsData(overrides);

  const tenantJobSearchProfile = await (async () => {
    const ownerId = getJobOwnerProfileId();
    if (
      !ownerId ||
      ownerId === DEFAULT_JOB_OWNER_PROFILE_ID ||
      ownerId === "__unmapped__"
    ) {
      return null;
    }
    const row = await profilesRepo.getProfileById(ownerId);
    if (!row?.data) return null;
    const parsed = jobSearchProfileSchema.safeParse(row.data);
    return parsed.success ? parsed.data : null;
  })();

  const result: Partial<AppSettings> = {
    ...envSettings,
  };

  const requestOwnerProfileId = getJobOwnerProfileId();

  const rawModel = overrides.model;
  const modelDef = settingsRegistry.model;
  const overrideModel = normalizeModelForProviderCompatibility(
    effectiveLlmProvider,
    modelDef.parse(rawModel),
  );
  const modelValue = overrideModel ?? resolvedModelDefault;

  for (const [key, def] of Object.entries(settingsRegistry)) {
    if (def.kind === "typed") {
      let rawOverride = overrides[key as settingsRepo.SettingKey];
      if (key === "searchCities" && !rawOverride) {
        rawOverride = overrides.jobspyLocation; // legacy fallback
      }

      let override = def.parse(rawOverride);
      let defaultValue = def.default();

      if (key === "jobSearchProfile" && tenantJobSearchProfile) {
        override = tenantJobSearchProfile;
      }

      if (key === "model") {
        defaultValue = resolvedModelDefault;
        override = overrideModel;
      }

      if (key === "llmBaseUrl") {
        const provider =
          effectiveLlmProvider ?? settingsRegistry.llmProvider.default();
        defaultValue =
          process.env.LLM_BASE_URL || resolveDefaultLlmBaseUrl(provider);
      }

      if (key === "resumeProjects") {
        let catalog: AppSettings["profileProjects"] = [];
        if (Object.keys(profile).length > 0) {
          try {
            catalog = extractProjectsFromResume(profile).catalog;
          } catch (error) {
            logger.warn(
              "Failed to extract projects from Reactive Resume data",
              {
                error,
              },
            );
          }
        }
        const resolved = resolveResumeProjectsSettings({
          catalog,
          overrideRaw: rawOverride ?? null,
        });
        result.profileProjects = resolved.profileProjects;
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        // biome-ignore lint/suspicious/noExplicitAny: dynamic assignment for settings building
        (result as any).resumeProjects = {
          value: resolved.resumeProjects,
          default: resolved.defaultResumeProjects,
          override: resolved.overrideResumeProjects,
        };
        continue;
      }

      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      // biome-ignore lint/suspicious/noExplicitAny: dynamic assignment for settings building
      (result as any)[key] = {
        value: override ?? defaultValue,
        default: defaultValue,
        override,
      };
    } else if (def.kind === "model") {
      const override =
        normalizeModelForProviderCompatibility(
          effectiveLlmProvider,
          overrides[key as settingsRepo.SettingKey] ?? null,
        ) ?? null;
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      // biome-ignore lint/suspicious/noExplicitAny: dynamic assignment for settings building
      (result as any)[key] = { value: override || modelValue, override };
    } else if (def.kind === "string") {
      if (!("envKey" in def) || !def.envKey) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        // biome-ignore lint/suspicious/noExplicitAny: dynamic assignment for settings building
        (result as any)[key] =
          overrides[key as settingsRepo.SettingKey] ?? null;
      }
    }
  }

  // In Basic Auth mode, the active search profile is derived from the request
  // context (username → basicAuthActiveProfileByUser), not the stored
  // activeProfileId row. Apply after the string-settings loop, which would
  // otherwise overwrite this field from the global settings table.
  if (
    tenantJobSearchProfile &&
    requestOwnerProfileId &&
    requestOwnerProfileId !== DEFAULT_JOB_OWNER_PROFILE_ID &&
    requestOwnerProfileId !== "__unmapped__"
  ) {
    result.activeProfileId = requestOwnerProfileId;
  }

  // Always expose the effective base resume id for the active RxResume mode.
  result.rxresumeBaseResumeId = rxresumeBaseResumeId;

  result.localResumeFileConfigured =
    Boolean(process.env.JOBOPS_LOCAL_RESUME_PATH?.trim()) ||
    Boolean((overrides.localResumeProfilePath ?? "").trim());

  return result as AppSettings;
}
