/**
 * Branding payload validation
 *
 * Shared by the two routers that accept a branding config: the per-organisation
 * one (`brandingSettings`) and the platform-wide one the Super Admin panel uses
 * (`superAdminBranding`). Kept here rather than duplicated so a new colour key
 * or scale value can never be accepted by one endpoint and rejected by the
 * other.
 */

import { body } from 'express-validator';
import {
  DEFAULT_BRANDING_COLORS,
  DEFAULT_BRANDING_LOGOS,
} from '../models/BrandingSettings';

const HEX = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
const COLOR_KEYS = Object.keys(DEFAULT_BRANDING_COLORS);
const LOGO_KEYS = Object.keys(DEFAULT_BRANDING_LOGOS);

export const APPEARANCE = ['light', 'dark', 'system'];
export const DENSITIES = ['compact', 'comfortable', 'spacious'];
export const RADII = ['none', 'sm', 'md', 'lg', 'xl'];
// Mirrors FontSizeScale — the same four steps as the profile-menu control.
export const FONT_SIZES = ['xs', 'sm', 'md', 'lg'];

/** Validate a `colors` object: every provided key must be known + a valid hex. */
export function validateColors(colors: any): string[] {
  const errs: string[] = [];
  if (colors === undefined) return errs;
  if (typeof colors !== 'object' || colors === null || Array.isArray(colors)) {
    return ['colors must be an object'];
  }
  for (const [key, value] of Object.entries(colors)) {
    if (!COLOR_KEYS.includes(key)) { errs.push(`Unknown color key: ${key}`); continue; }
    if (typeof value !== 'string' || !HEX.test(value)) {
      errs.push(`Invalid HEX color for ${key}: ${String(value)}`);
    }
  }
  return errs;
}

/** Validation rules for PUT — enums checked here, colors checked in-handler. */
export const brandingUpdateValidation = [
  body('appearanceMode').optional().isIn(APPEARANCE).withMessage('Invalid appearance mode'),
  body('density').optional().isIn(DENSITIES).withMessage('Invalid density'),
  body('borderRadius').optional().isIn(RADII).withMessage('Invalid border radius'),
  body('fontSize').optional().isIn(FONT_SIZES).withMessage('Invalid font size'),
  body('lightThemePreset').optional().isString(),
  body('darkThemePreset').optional().isString(),
  body('themePreset').optional().isString(), // legacy — seeds both when the new fields are absent
  body('colors').optional().isObject(),
  body('logos').optional().isObject(),
];

/** Keep only known, well-typed fields from an arbitrary payload (PUT / import). */
export function sanitizeBrandingPayload(input: any): { data: any; errors: string[] } {
  const errors: string[] = [];
  const data: any = {};

  if (input.colors !== undefined) {
    const colorErrs = validateColors(input.colors);
    errors.push(...colorErrs);
    if (colorErrs.length === 0) {
      data.colors = {};
      for (const k of COLOR_KEYS) if (input.colors[k] !== undefined) data.colors[k] = input.colors[k];
    }
  }
  // `layoutWidth` and `typography` were retired with the Branding page
  // simplification — silently dropped rather than rejected, so older clients
  // and previously exported files still import cleanly.
  if (input.logos !== undefined) {
    if (typeof input.logos !== 'object' || input.logos === null) errors.push('logos must be an object');
    else {
      data.logos = {};
      for (const k of LOGO_KEYS) if (input.logos[k] !== undefined) {
        if (typeof input.logos[k] !== 'string') errors.push(`logos.${k} must be a string`);
        else data.logos[k] = input.logos[k];
      }
    }
  }
  // Theme presets: per-appearance fields, with the legacy single `themePreset`
  // still accepted (older clients / exported files) seeding whichever is absent.
  let legacyPreset = '';
  if (input.themePreset !== undefined) {
    if (typeof input.themePreset !== 'string') errors.push('themePreset must be a string');
    else legacyPreset = input.themePreset;
  }
  for (const key of ['lightThemePreset', 'darkThemePreset'] as const) {
    if (input[key] !== undefined) {
      if (typeof input[key] !== 'string') errors.push(`${key} must be a string`);
      else data[key] = input[key];
    } else if (legacyPreset) {
      data[key] = legacyPreset;
    }
  }
  if (input.appearanceMode !== undefined) {
    if (!APPEARANCE.includes(input.appearanceMode)) errors.push('Invalid appearance mode');
    else data.appearanceMode = input.appearanceMode;
  }
  if (input.density !== undefined) {
    if (!DENSITIES.includes(input.density)) errors.push('Invalid density');
    else data.density = input.density;
  }
  if (input.borderRadius !== undefined) {
    if (!RADII.includes(input.borderRadius)) errors.push('Invalid border radius');
    else data.borderRadius = input.borderRadius;
  }
  if (input.fontSize !== undefined) {
    if (!FONT_SIZES.includes(input.fontSize)) errors.push('Invalid font size');
    else data.fontSize = input.fontSize;
  }

  return { data, errors };
}

/** Merge structured sections onto an existing config (deep-merge colors/logos). */
export function mergeBrandingSections(existing: any, incoming: any): any {
  const merged: any = { ...incoming };
  for (const section of ['colors', 'logos']) {
    if (incoming[section] !== undefined) {
      const cur = existing?.[section]?.toObject?.() || existing?.[section] || {};
      merged[section] = { ...cur, ...incoming[section] };
    }
  }
  return merged;
}
