/**
 * Importador de projetos via JSON — M9.7
 *
 * Lê um arquivo JSON exportado do VentoApp (roundtrip com `storage.ts`)
 * e atualiza o store Zustand correspondente.
 *
 * Suporta dois formatos:
 *  1. SavedProject (formato IndexedDB)
 *     { name, module, inputs, createdAt, updatedAt }
 *  2. Snapshot direto do windStore (formato debug "Exportar estado atual")
 *     { v0, s1, s2, s3, vk, q, ... }
 *
 * Valida estrutura mínima antes de aplicar; retorna erros tipados.
 */

import { useWindStore } from '../store/appStore';
import { useGalpaoStore } from '../store/galpaoStore';
import type { SavedProject } from './storage';
import type { TerrainCategory } from './wind-kernel';

export type ModuleId = SavedProject['module'];

export interface ImportResult {
  ok: boolean;
  module?: ModuleId;
  projectName?: string;
  appliedFields?: string[];
  warnings?: string[];
  error?: string;
}

const VALID_MODULES: readonly ModuleId[] = [
  'galpao',
  'cilindro',
  'vault',
  'dome',
  'sign',
  'isolated-roof',
  'bar',
  'bridge',
  'dynamics',
];

const VALID_CATEGORIES: readonly TerrainCategory[] = ['I', 'II', 'III', 'IV', 'V'];

function isString(v: unknown): v is string {
  return typeof v === 'string';
}

function isNumber(v: unknown): v is number {
  return typeof v === 'number' && Number.isFinite(v);
}

function isBoolean(v: unknown): v is boolean {
  return typeof v === 'boolean';
}

function isObject(v: unknown): v is Record<string, unknown> {
  return typeof v === 'object' && v !== null && !Array.isArray(v);
}

/**
 * Detecta o tipo de arquivo importado.
 *
 *  - Se tem `module` e `inputs` → SavedProject
 *  - Se tem `v0` e `terrainCategory` → Snapshot do windStore
 *  - Caso contrário → inválido
 */
export function detectFormat(parsed: unknown): 'saved-project' | 'snapshot' | 'unknown' {
  if (!isObject(parsed)) return 'unknown';
  if (isString(parsed.module) && isObject(parsed.inputs)) return 'saved-project';
  if ('v0' in parsed && ('terrainCategory' in parsed || 's2' in parsed)) return 'snapshot';
  return 'unknown';
}

/**
 * Valida um SavedProject.
 *
 * Retorna warnings (não-fatais) e erro (fatal) separadamente.
 */
export function validateSavedProject(raw: unknown): {
  ok: boolean;
  warnings: string[];
  errors: string[];
} {
  const warnings: string[] = [];
  const errors: string[] = [];

  if (!isObject(raw)) {
    errors.push('JSON não é um objeto.');
    return { ok: false, warnings, errors };
  }

  if (!isString(raw.name)) {
    errors.push('Campo "name" ausente ou não é string.');
  }
  if (!isString(raw.module) || !VALID_MODULES.includes(raw.module as ModuleId)) {
    errors.push(`Campo "module" ausente ou inválido (deve ser um de: ${VALID_MODULES.join(', ')}).`);
  }
  if (!isObject(raw.inputs)) {
    errors.push('Campo "inputs" ausente ou não é objeto.');
  }
  if (!isNumber(raw.createdAt)) {
    warnings.push('Campo "createdAt" ausente — será gerado automaticamente.');
  }
  if (!isNumber(raw.updatedAt)) {
    warnings.push('Campo "updatedAt" ausente — será gerado automaticamente.');
  }

  return { ok: errors.length === 0, warnings, errors };
}

/**
 * Valida um snapshot do windStore.
 */
export function validateSnapshot(raw: unknown): {
  ok: boolean;
  warnings: string[];
  errors: string[];
} {
  const warnings: string[] = [];
  const errors: string[] = [];

  if (!isObject(raw)) {
    errors.push('JSON não é um objeto.');
    return { ok: false, warnings, errors };
  }

  if (!isNumber(raw.v0)) errors.push('Campo "v0" ausente ou não é número.');
  if (!isNumber(raw.s1)) errors.push('Campo "s1" ausente ou não é número.');
  if (!isNumber(raw.s3)) errors.push('Campo "s3" ausente ou não é número.');
  if (
    !isString(raw.terrainCategory) ||
    !VALID_CATEGORIES.includes(raw.terrainCategory as TerrainCategory)
  ) {
    errors.push(
      `Campo "terrainCategory" inválido (deve ser um de: ${VALID_CATEGORIES.join(', ')}).`,
    );
  }
  if (!isNumber(raw.s3Group)) warnings.push('Campo "s3Group" ausente — mantendo valor padrão.');
  if (!isNumber(raw.largestDimension))
    warnings.push('Campo "largestDimension" ausente — mantendo valor padrão.');
  if (!isNumber(raw.heightZ)) warnings.push('Campo "heightZ" ausente — mantendo valor padrão.');

  return { ok: errors.length === 0, warnings, errors };
}

/**
 * Parseia uma string JSON com segurança.
 */
export function parseProjectJson(text: string): unknown {
  try {
    return JSON.parse(text);
  } catch (e) {
    throw new Error(`JSON inválido: ${e instanceof Error ? e.message : 'erro desconhecido'}`);
  }
}

/**
 * Aplica um SavedProject validado aos stores Zustand.
 *
 * Apenas o windStore e galpaoStore são atualizados neste MVP;
 * módulos futuros podem estender via dispatcher.
 */
export function applySavedProject(project: SavedProject): ImportResult {
  const appliedFields: string[] = [];
  const warnings: string[] = [];

  const wind = useWindStore.getState();
  const inputs = project.inputs as Record<string, unknown>;

  // Atualiza windStore se o snapshot estiver presente
  if ('wind' in inputs && isObject(inputs.wind)) {
    const w = inputs.wind;
    if (isNumber(w.v0)) {
      wind.setV0(w.v0);
      appliedFields.push('wind.v0');
    }
    if (isNumber(w.s1)) {
      wind.setS1(w.s1);
      appliedFields.push('wind.s1');
    }
    if (isNumber(w.terrainCategory) || isString(w.terrainCategory)) {
      const cat = String(w.terrainCategory);
      if (VALID_CATEGORIES.includes(cat as TerrainCategory)) {
        wind.setTerrainCategory(cat as TerrainCategory);
        appliedFields.push('wind.terrainCategory');
      } else {
        warnings.push(`Categoria inválida: ${cat}`);
      }
    }
    if (isNumber(w.s3Group)) {
      wind.setS3Group(w.s3Group as 1 | 2 | 3 | 4 | 5);
      appliedFields.push('wind.s3Group');
    }
    if (isNumber(w.largestDimension) && isNumber(w.heightZ)) {
      wind.setDimensions(w.largestDimension, w.heightZ);
      appliedFields.push('wind.dimensions');
    }
  }

  // Atualiza galpaoStore se inputs do galpão
  if (project.module === 'galpao' && 'galpao' in inputs && isObject(inputs.galpao)) {
    const g = inputs.galpao;
    const galpao = useGalpaoStore.getState();
    if (isNumber(g.width)) {
      galpao.setWidth(g.width);
      appliedFields.push('galpao.width');
    }
    if (isNumber(g.length)) {
      galpao.setLength(g.length);
      appliedFields.push('galpao.length');
    }
    if (isNumber(g.height)) {
      galpao.setHeight(g.height);
      appliedFields.push('galpao.height');
    }
    if (isNumber(g.roofPitch)) {
      galpao.setRoofPitch(g.roofPitch);
      appliedFields.push('galpao.roofPitch');
    }
    if (isNumber(g.windAngle) || (g.windAngle === 0 || g.windAngle === 90)) {
      wind.setWindAngle((g.windAngle as 0 | 90));
      appliedFields.push('wind.windAngle');
    }
    if (isString(g.permeabilityCase)) {
      wind.setPermeabilityCase(g.permeabilityCase as 'four-equally-permeable' | 'dominant-windward');
      appliedFields.push('wind.permeabilityCase');
    }
    if (isNumber(g.cpiRatio)) {
      wind.setCpiRatio(g.cpiRatio);
      appliedFields.push('wind.cpiRatio');
    }
  }

  return {
    ok: true,
    module: project.module,
    projectName: project.name,
    appliedFields,
    warnings,
  };
}

/**
 * Aplica um snapshot do windStore.
 */
export function applySnapshot(snapshot: Record<string, unknown>): ImportResult {
  const appliedFields: string[] = [];
  const warnings: string[] = [];

  const wind = useWindStore.getState();
  if (isNumber(snapshot.v0)) {
    wind.setV0(snapshot.v0);
    appliedFields.push('v0');
  }
  if (isNumber(snapshot.s1)) {
    wind.setS1(snapshot.s1);
    appliedFields.push('s1');
  }
  if (isNumber(snapshot.s3)) {
    wind.setS3(snapshot.s3);
    appliedFields.push('s3');
  }
  if (isString(snapshot.terrainCategory)) {
    if (VALID_CATEGORIES.includes(snapshot.terrainCategory as TerrainCategory)) {
      wind.setTerrainCategory(snapshot.terrainCategory as TerrainCategory);
      appliedFields.push('terrainCategory');
    } else {
      warnings.push(`Categoria inválida: ${snapshot.terrainCategory}`);
    }
  }
  if (isNumber(snapshot.s3Group)) {
    wind.setS3Group(snapshot.s3Group as 1 | 2 | 3 | 4 | 5);
    appliedFields.push('s3Group');
  }
  if (isNumber(snapshot.largestDimension) && isNumber(snapshot.heightZ)) {
    wind.setDimensions(snapshot.largestDimension, snapshot.heightZ);
    appliedFields.push('dimensions');
  }

  return { ok: true, appliedFields, warnings };
}

/**
 * Atalho: parseia texto JSON, detecta formato, valida, aplica.
 */
export function importProjectFromText(text: string): ImportResult {
  let parsed: unknown;
  try {
    parsed = parseProjectJson(text);
  } catch (e) {
    return { ok: false, error: e instanceof Error ? e.message : 'Erro ao parsear JSON' };
  }

  const format = detectFormat(parsed);

  if (format === 'saved-project') {
    const validation = validateSavedProject(parsed);
    if (!validation.ok) {
      return {
        ok: false,
        error: `Validação falhou: ${validation.errors.join('; ')}`,
        warnings: validation.warnings,
      };
    }
    const project = parsed as SavedProject;
    const result = applySavedProject(project);
    return { ...result, warnings: [...(result.warnings ?? []), ...validation.warnings] };
  }

  if (format === 'snapshot') {
    const validation = validateSnapshot(parsed);
    if (!validation.ok) {
      return {
        ok: false,
        error: `Validação falhou: ${validation.errors.join('; ')}`,
        warnings: validation.warnings,
      };
    }
    const result = applySnapshot(parsed as Record<string, unknown>);
    return { ...result, warnings: [...(result.warnings ?? []), ...validation.warnings] };
  }

  return {
    ok: false,
    error:
      'Formato não reconhecido. Esperado: SavedProject (com module/inputs) ou snapshot do windStore.',
  };
}

/**
 * Cria um File picker e dispara callback com o conteúdo lido.
 */
export function readProjectFile(file: File): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(typeof reader.result === 'string' ? reader.result : '');
    reader.onerror = () => reject(reader.error ?? new Error('Falha ao ler arquivo'));
    reader.readAsText(file);
  });
}

/**
 * Exporta um projeto para string JSON (roundtrip).
 * Útil para testes.
 */
export function exportProjectToJson(project: SavedProject): string {
  return JSON.stringify(project, null, 2);
}

/**
 * Serialização determinística para snapshot do windStore.
 */
export function snapshotWindStoreToJson(): string {
  const state = useWindStore.getState();
  const snapshot = {
    v0: state.v0,
    s1: state.s1,
    s3: state.s3,
    s3Group: state.s3Group,
    terrainCategory: state.terrainCategory,
    largestDimension: state.largestDimension,
    heightZ: state.heightZ,
    s2: state.s2,
    vk: state.vk,
    q: state.q,
    structureClass: state.structureClass,
  };
  return JSON.stringify(snapshot, null, 2);
}

/**
 * Detecção redundante para o módulo unimported (evita warning em build).
 */
export const _internals = {
  VALID_MODULES,
  VALID_CATEGORIES,
  isString,
  isNumber,
  isBoolean,
  isObject,
};