// Per-(definition, project) settings for branded intake artifacts, agent
// overrides, auto-approval rules, and share-link toggles. See spec
// `specs/active/workflow-intake-artifact-ux.md` §5.2.
//
// Read path is hot: engine.startCase() loads the row to snapshot it into
// context_json. Write path is rare (operator opens settings drawer). No
// per-row lock — last writer wins.

import { getDb, newId, nowIso } from '../db.js';
import { getDefinition } from './definitions.js';
import { getBrandProfile } from '../brand.js';
import { pickBrandAccent } from './util.js';

// Brand inherits from the global /settings/brand profile (lib/brand.js).
// Per-(definition, project) settings ONLY hold workflow-specific
// presentation overrides:
//   - sender_name      — override the brand's company_name on the form
//   - cover_image_url  — workflow-specific hero image
//   - cover_copy       — title + subtitle of the form's hero
//   - accent_hex       — optional override for the brand's primary accent
//   - logo_url         — optional override for the brand's logo
// All other brand attributes (fonts, voice, presentation_style) flow through
// from the global brand profile and are NOT duplicated here. The engine
// snapshots the merged result at startCase so in-flight cases are immune
// to subsequent brand edits.
const PRESENTATION_DEFAULTS = Object.freeze({
  sender_name: null,
  cover_image_url: null,
  cover_copy: { title: null, subtitle: null },
  accent_hex: null,
  logo_url: null,
});

// Resolve the merged brand+presentation for an artifact renderer. Callers
// should hit this at SNAPSHOT time (engine.startCase) so the case binds to
// the brand-as-it-was-then. Re-resolving on every artifact render would
// silently mutate in-flight cases when the operator edits /settings/brand.
export function resolveBrandPresentation({ settings = null } = {}) {
  const brand = getBrandProfile();
  const overrides = settings?.brand || {};
  const accent = overrides.accent_hex || pickBrandAccent(brand);
  const logo = overrides.logo_url || brand.logo_data_url || brand.logo_path || null;
  const sender = overrides.sender_name || brand.company_name || 'Madarx OS';
  return {
    accent_hex: accent,
    sender_name: sender,
    logo_url: logo,
    cover_image_url: overrides.cover_image_url || null,
    cover_copy: {
      title: overrides.cover_copy?.title || null,
      subtitle: overrides.cover_copy?.subtitle || null,
    },
    fonts: Array.isArray(brand.fonts) ? brand.fonts : [],
    tagline: brand.tagline || null,
    company_name: brand.company_name || null,
    voice: Array.isArray(brand.voice) ? brand.voice : [],
  };
}

function parseJsonField(value, fallback) {
  if (value == null || value === '') return fallback;
  try { return JSON.parse(value); } catch { return fallback; }
}

function rowToSettings(row) {
  if (!row) return null;
  return {
    id: row.id,
    definition_id: row.definition_id,
    project_id: row.project_id,
    default_for_project: row.default_for_project === 1,
    // `brand` here is the per-workflow PRESENTATION OVERRIDES only. The
    // global brand from /settings/brand is merged in via
    // resolveBrandPresentation at snapshot time. Keep `brand` as the field
    // name for backwards compat with consumers that read settings.brand.
    brand: { ...PRESENTATION_DEFAULTS, ...parseJsonField(row.brand_json, {}) },
    template_html: row.template_html || null,
    template_version: row.template_version || 1,
    agent_overrides: parseJsonField(row.agent_overrides_json, {}),
    auto_approve_rules: parseJsonField(row.auto_approve_rules_json, []),
    share_link_enabled: row.share_link_enabled === 1,
    share_link_default_expiry_days: row.share_link_default_expiry_days || 14,
    created_by_user_id: row.created_by_user_id,
    created_at: row.created_at,
    updated_at: row.updated_at,
  };
}

export function getSettings({ definition_id, project_id }) {
  if (!definition_id || !project_id) return null;
  const row = getDb()
    .prepare('SELECT * FROM process_definition_settings WHERE definition_id = ? AND project_id = ?')
    .get(definition_id, project_id);
  return rowToSettings(row);
}

export function getSettingsBySlug({ definition_slug, project_id }) {
  const definition = getDefinition(definition_slug);
  if (!definition) return { definition: null, settings: null };
  return { definition, settings: getSettings({ definition_id: definition.id, project_id }) };
}

export function listSettingsForProject(project_id) {
  if (!project_id) return [];
  const rows = getDb()
    .prepare('SELECT * FROM process_definition_settings WHERE project_id = ? ORDER BY updated_at DESC')
    .all(project_id);
  return rows.map(rowToSettings).filter(Boolean);
}

// Upsert (definition, project) settings. Validates structural shape but does
// NOT validate brand_url reachability or agent slug existence — those are
// surfaced later. Returns the resulting row.
export function upsertSettings({
  definition_id,
  project_id,
  default_for_project = null,
  brand = null,
  template_html = null,
  agent_overrides = null,
  auto_approve_rules = null,
  share_link_enabled = null,
  share_link_default_expiry_days = null,
  user_id = null,
}) {
  if (!definition_id) throw new Error('upsertSettings: definition_id required');
  if (!project_id) throw new Error('upsertSettings: project_id required');
  // Structural validation
  if (brand !== null && (typeof brand !== 'object' || Array.isArray(brand))) {
    throw new Error('upsertSettings: brand must be an object');
  }
  if (agent_overrides !== null && (typeof agent_overrides !== 'object' || Array.isArray(agent_overrides))) {
    throw new Error('upsertSettings: agent_overrides must be an object');
  }
  if (auto_approve_rules !== null && !Array.isArray(auto_approve_rules)) {
    throw new Error('upsertSettings: auto_approve_rules must be an array');
  }
  if (share_link_default_expiry_days !== null) {
    const n = Number(share_link_default_expiry_days);
    if (!Number.isInteger(n) || n < 1 || n > 365) {
      throw new Error('upsertSettings: share_link_default_expiry_days must be an integer 1..365');
    }
  }

  const db = getDb();
  const now = nowIso();
  const existing = getDb()
    .prepare('SELECT * FROM process_definition_settings WHERE definition_id = ? AND project_id = ?')
    .get(definition_id, project_id);

  if (existing) {
    db.prepare(`
      UPDATE process_definition_settings
         SET default_for_project = COALESCE(?, default_for_project),
             brand_json = COALESCE(?, brand_json),
             template_html = COALESCE(?, template_html),
             template_version = template_version + (CASE WHEN ? IS NOT NULL THEN 1 ELSE 0 END),
             agent_overrides_json = COALESCE(?, agent_overrides_json),
             auto_approve_rules_json = COALESCE(?, auto_approve_rules_json),
             share_link_enabled = COALESCE(?, share_link_enabled),
             share_link_default_expiry_days = COALESCE(?, share_link_default_expiry_days),
             updated_at = ?
       WHERE id = ?
    `).run(
      default_for_project == null ? null : default_for_project ? 1 : 0,
      brand == null ? null : JSON.stringify({ ...PRESENTATION_DEFAULTS, ...brand }),
      template_html,
      template_html, // version-bump trigger
      agent_overrides == null ? null : JSON.stringify(agent_overrides),
      auto_approve_rules == null ? null : JSON.stringify(auto_approve_rules),
      share_link_enabled == null ? null : share_link_enabled ? 1 : 0,
      share_link_default_expiry_days == null ? null : Number(share_link_default_expiry_days),
      now,
      existing.id,
    );
  } else {
    db.prepare(`
      INSERT INTO process_definition_settings
        (id, definition_id, project_id, default_for_project, brand_json,
         template_html, template_version, agent_overrides_json,
         auto_approve_rules_json, share_link_enabled, share_link_default_expiry_days,
         created_by_user_id, created_at, updated_at)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `).run(
      newId('pds'),
      definition_id,
      project_id,
      default_for_project ? 1 : 0,
      brand == null ? null : JSON.stringify({ ...PRESENTATION_DEFAULTS, ...brand }),
      template_html,
      1,
      agent_overrides == null ? null : JSON.stringify(agent_overrides),
      auto_approve_rules == null ? null : JSON.stringify(auto_approve_rules),
      share_link_enabled ? 1 : 0,
      share_link_default_expiry_days == null ? 14 : Number(share_link_default_expiry_days),
      user_id,
      now,
      now,
    );
  }

  return getSettings({ definition_id, project_id });
}

export function deleteSettings({ definition_id, project_id }) {
  if (!definition_id || !project_id) return false;
  const res = getDb()
    .prepare('DELETE FROM process_definition_settings WHERE definition_id = ? AND project_id = ?')
    .run(definition_id, project_id);
  return res.changes > 0;
}

export const PRESENTATION_DEFAULT = PRESENTATION_DEFAULTS;
