import fs from 'fs';
import path from 'path';
import { resolveRuntimePath } from '../runtime-paths';

export type ServerHttpProtocol = 'http' | 'https';

export interface LinkPreviewConfig {
  enabled: boolean;
  cacheTtlMinutes: number;
  maxCacheSizeMb: number;
}

export interface OpenApiDocsConfig {
  enabled: boolean;
}

export interface ServerVariablesConfig {
  klipyApiKey: string;
  rawgApiKey: string;
  releaseManifestUrl: string;
  serverPort: number;
  serverProtocol: ServerHttpProtocol;
  serverHost: string;
  linkPreview: LinkPreviewConfig;
  openApiDocs: OpenApiDocsConfig;
}

const DATA_DIR = resolveRuntimePath('data');
const VARIABLES_FILE = path.join(DATA_DIR, 'variables.json');
const DEFAULT_SERVER_PORT = 3001;
const DEFAULT_SERVER_PROTOCOL: ServerHttpProtocol = 'http';
const DEFAULT_LINK_PREVIEW_CACHE_TTL_MINUTES = 7200;
const DEFAULT_LINK_PREVIEW_MAX_CACHE_SIZE_MB = 50;
const HARD_MAX_CACHE_SIZE_MB = 50;

function normalizeKlipyApiKey(value: unknown): string {
  return typeof value === 'string' ? value.trim() : '';
}

function normalizeRawgApiKey(value: unknown): string {
  return typeof value === 'string' ? value.trim() : '';
}

function normalizeReleaseManifestUrl(value: unknown): string {
  return typeof value === 'string' ? value.trim() : '';
}

function normalizeServerHost(value: unknown): string {
  return typeof value === 'string' ? value.trim() : '';
}

function normalizeServerProtocol(
  value: unknown,
  fallback: ServerHttpProtocol = DEFAULT_SERVER_PROTOCOL
): ServerHttpProtocol {
  if (typeof value === 'boolean') {
    return value ? 'https' : 'http';
  }

  if (typeof value !== 'string') {
    return fallback;
  }

  const normalized = value.trim().toLowerCase();

  if (normalized === 'https' || normalized === 'true') {
    return 'https';
  }

  if (normalized === 'http' || normalized === 'false') {
    return 'http';
  }

  return fallback;
}

function normalizeServerPort(value: unknown, fallback = DEFAULT_SERVER_PORT): number {
  const parsed = typeof value === 'number'
    ? value
    : typeof value === 'string'
      ? Number.parseInt(value.trim(), 10)
      : Number.NaN;

  return Number.isInteger(parsed) && parsed >= 1 && parsed <= 65535
    ? parsed
    : fallback;
}

function normalizeLinkPreviewConfig(value: unknown): LinkPreviewConfig {
  const raw = (value && typeof value === 'object' && !Array.isArray(value))
    ? value as Record<string, unknown>
    : {};
  const enabled = typeof raw.enabled === 'boolean'
    ? raw.enabled
    : true;
  const cacheTtl = typeof raw.cacheTtlMinutes === 'number'
    && Number.isFinite(raw.cacheTtlMinutes)
    && raw.cacheTtlMinutes >= 0
    ? raw.cacheTtlMinutes
    : DEFAULT_LINK_PREVIEW_CACHE_TTL_MINUTES;
  const maxSize = typeof raw.maxCacheSizeMb === 'number'
    && Number.isFinite(raw.maxCacheSizeMb)
    && raw.maxCacheSizeMb >= 0
    ? Math.min(raw.maxCacheSizeMb, HARD_MAX_CACHE_SIZE_MB)
    : DEFAULT_LINK_PREVIEW_MAX_CACHE_SIZE_MB;

  return { enabled, cacheTtlMinutes: cacheTtl, maxCacheSizeMb: maxSize };
}

function normalizeOpenApiDocsConfig(value: unknown): OpenApiDocsConfig {
  const raw = (value && typeof value === 'object' && !Array.isArray(value))
    ? value as Record<string, unknown>
    : {};

  return { enabled: raw.enabled === true };
}

function hasEnvironmentOverride(value: string | undefined): value is string {
  return typeof value === 'string' && value.trim().length > 0;
}

function readRawVariables(): { rawContents: string; parsed: Record<string, unknown> } {
  if (!fs.existsSync(VARIABLES_FILE)) {
    return { rawContents: '', parsed: {} };
  }

  const rawContents = fs.readFileSync(VARIABLES_FILE, 'utf8');

  if (!rawContents.trim()) {
    return { rawContents, parsed: {} };
  }

  try {
    const parsed = JSON.parse(rawContents) as unknown;

    if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
      return { rawContents, parsed: parsed as Record<string, unknown> };
    }
  } catch (error) {
    console.warn('[Config] Failed to parse variables.json. Recreating it with defaults.', error);
  }

  return { rawContents, parsed: {} };
}

export function getVariablesConfigPath(): string {
  return VARIABLES_FILE;
}

export function ensureVariablesConfig(): ServerVariablesConfig {
  if (!fs.existsSync(DATA_DIR)) {
    fs.mkdirSync(DATA_DIR, { recursive: true });
  }

  const { rawContents, parsed } = readRawVariables();
  const { serverIpAddress: legacyServerIpAddress, ...remainingParsed } = parsed;
  const normalized = {
    ...remainingParsed,
    klipyApiKey: normalizeKlipyApiKey(remainingParsed.klipyApiKey),
    rawgApiKey: normalizeRawgApiKey(remainingParsed.rawgApiKey),
    releaseManifestUrl: normalizeReleaseManifestUrl(remainingParsed.releaseManifestUrl),
    serverPort: normalizeServerPort(remainingParsed.serverPort),
    serverProtocol: normalizeServerProtocol(remainingParsed.serverProtocol),
    serverHost: normalizeServerHost(remainingParsed.serverHost ?? legacyServerIpAddress),
    linkPreview: normalizeLinkPreviewConfig(remainingParsed.linkPreview),
    openApiDocs: normalizeOpenApiDocsConfig(remainingParsed.openApiDocs)
  };
  const nextContents = JSON.stringify(normalized, null, 2) + '\n';

  if (!fs.existsSync(VARIABLES_FILE) || rawContents !== nextContents) {
    fs.writeFileSync(VARIABLES_FILE, nextContents, 'utf8');
  }

  return {
    klipyApiKey: normalized.klipyApiKey,
    rawgApiKey: normalized.rawgApiKey,
    releaseManifestUrl: normalized.releaseManifestUrl,
    serverPort: normalized.serverPort,
    serverProtocol: normalized.serverProtocol,
    serverHost: normalized.serverHost,
    linkPreview: normalized.linkPreview,
    openApiDocs: normalized.openApiDocs
  };
}

export function getVariablesConfig(): ServerVariablesConfig {
  return ensureVariablesConfig();
}

export function getKlipyApiKey(): string {
  return getVariablesConfig().klipyApiKey;
}

export function getRawgApiKey(): string {
  if (hasEnvironmentOverride(process.env.RAWG_API_KEY)) {
    return process.env.RAWG_API_KEY.trim();
  }

  return getVariablesConfig().rawgApiKey;
}

export function hasKlipyApiKey(): boolean {
  return getKlipyApiKey().length > 0;
}

export function getReleaseManifestUrl(): string {
  return getVariablesConfig().releaseManifestUrl;
}

export function getServerProtocol(): ServerHttpProtocol {
  if (hasEnvironmentOverride(process.env.SSL)) {
    return normalizeServerProtocol(process.env.SSL);
  }

  return getVariablesConfig().serverProtocol;
}

export function getServerPort(): number {
  if (hasEnvironmentOverride(process.env.PORT)) {
    return normalizeServerPort(process.env.PORT);
  }

  return getVariablesConfig().serverPort;
}

export function getServerHost(): string | undefined {
  const serverHost = getVariablesConfig().serverHost;

  return serverHost || undefined;
}

export function isHttpsServerEnabled(): boolean {
  return getServerProtocol() === 'https';
}

export function areOpenApiDocsEnabled(): boolean {
  if (hasEnvironmentOverride(process.env.OPENAPI_DOCS_ENABLED)) {
    return process.env.OPENAPI_DOCS_ENABLED.trim().toLowerCase() === 'true';
  }

  return getVariablesConfig().openApiDocs.enabled;
}

export function setOpenApiDocsEnabled(enabled: boolean): OpenApiDocsConfig {
  if (!fs.existsSync(DATA_DIR)) {
    fs.mkdirSync(DATA_DIR, { recursive: true });
  }

  const { parsed } = readRawVariables();
  const next = {
    ...parsed,
    openApiDocs: { enabled }
  };

  fs.writeFileSync(VARIABLES_FILE, JSON.stringify(next, null, 2) + '\n', 'utf8');
  ensureVariablesConfig();

  return { enabled };
}

export function getLinkPreviewConfig(): LinkPreviewConfig {
  return getVariablesConfig().linkPreview;
}
