/**
 * Testes do importador de projetos (M9.7).
 *
 * Valida parsing, detecção de formato, validação, e aplicação
 * idempotente aos stores Zustand (mockados).
 */

import { describe, it, expect, beforeEach, vi } from 'vitest';

vi.mock('../../store/appStore', () => ({
  useWindStore: {
    getState: () => ({
      v0: 40,
      s1: 1,
      s3: 1,
      s3Group: 3,
      terrainCategory: 'II',
      largestDimension: 30,
      heightZ: 10,
      structureClass: 'B',
      s2: 1.06,
      vk: 42.4,
      q: 1.1024,
      setV0: vi.fn(),
      setS1: vi.fn(),
      setS3: vi.fn(),
      setS3Group: vi.fn(),
      setTerrainCategory: vi.fn(),
      setDimensions: vi.fn(),
      setWindAngle: vi.fn(),
      setPermeabilityCase: vi.fn(),
      setCpiRatio: vi.fn(),
    }),
  },
}));

vi.mock('../../store/galpaoStore', () => ({
  useGalpaoStore: {
    getState: () => ({
      width: 15,
      length: 30,
      height: 6,
      roofPitch: 10,
      windAngle: 0,
      permeabilityCase: 'four-equally-permeable',
      cpiRatio: 1,
      setWidth: vi.fn(),
      setLength: vi.fn(),
      setHeight: vi.fn(),
      setRoofPitch: vi.fn(),
    }),
  },
}));

import {
  parseProjectJson,
  detectFormat,
  validateSavedProject,
  validateSnapshot,
  applySavedProject,
  applySnapshot,
  importProjectFromText,
  exportProjectToJson,
  snapshotWindStoreToJson,
} from '../import-project';
import type { SavedProject } from '../storage';

describe('M9.7 — parseProjectJson', () => {
  it('Parseia JSON válido', () => {
    const result = parseProjectJson('{"a": 1}');
    expect(result).toEqual({ a: 1 });
  });

  it('Lança erro em JSON inválido', () => {
    expect(() => parseProjectJson('{')).toThrow();
  });

  it('Lança erro em string vazia', () => {
    expect(() => parseProjectJson('')).toThrow();
  });
});

describe('M9.7 — detectFormat', () => {
  it('Detecta SavedProject', () => {
    expect(detectFormat({ module: 'galpao', inputs: {} })).toBe('saved-project');
  });

  it('Detecta snapshot do windStore', () => {
    expect(detectFormat({ v0: 40, terrainCategory: 'II' })).toBe('snapshot');
  });

  it('Retorna unknown para objeto vazio', () => {
    expect(detectFormat({})).toBe('unknown');
  });

  it('Retorna unknown para null', () => {
    expect(detectFormat(null)).toBe('unknown');
  });

  it('Retorna unknown para array', () => {
    expect(detectFormat([])).toBe('unknown');
  });
});

describe('M9.7 — validateSavedProject', () => {
  it('Aceita SavedProject válido', () => {
    const project = {
      name: 'Galpão Teste',
      module: 'galpao',
      inputs: {},
      createdAt: 1000,
      updatedAt: 2000,
    };
    const v = validateSavedProject(project);
    expect(v.ok).toBe(true);
    expect(v.errors).toHaveLength(0);
  });

  it('Rejeita projeto sem name', () => {
    const project = { module: 'galpao', inputs: {} };
    const v = validateSavedProject(project);
    expect(v.ok).toBe(false);
    expect(v.errors.some((e) => e.includes('name'))).toBe(true);
  });

  it('Rejeita módulo inválido', () => {
    const project = { name: 'X', module: 'invalido', inputs: {} };
    const v = validateSavedProject(project);
    expect(v.ok).toBe(false);
    expect(v.errors.some((e) => e.includes('module'))).toBe(true);
  });

  it('Rejeita inputs não-objeto', () => {
    const project = { name: 'X', module: 'galpao', inputs: 'não-objeto' };
    const v = validateSavedProject(project);
    expect(v.ok).toBe(false);
    expect(v.errors.some((e) => e.includes('inputs'))).toBe(true);
  });

  it('Emite warning se timestamps faltarem', () => {
    const project = { name: 'X', module: 'galpao', inputs: {} };
    const v = validateSavedProject(project);
    expect(v.warnings.length).toBeGreaterThan(0);
  });
});

describe('M9.7 — validateSnapshot', () => {
  it('Aceita snapshot válido', () => {
    const snap = { v0: 40, s1: 1, s3: 1, terrainCategory: 'II' };
    const v = validateSnapshot(snap);
    expect(v.ok).toBe(true);
  });

  it('Rejeita v0 ausente', () => {
    const v = validateSnapshot({ s1: 1, s3: 1, terrainCategory: 'II' });
    expect(v.ok).toBe(false);
    expect(v.errors.some((e) => e.includes('v0'))).toBe(true);
  });

  it('Rejeita categoria inválida', () => {
    const v = validateSnapshot({ v0: 40, s1: 1, s3: 1, terrainCategory: 'VI' });
    expect(v.ok).toBe(false);
    expect(v.errors.some((e) => e.includes('terrainCategory'))).toBe(true);
  });

  it('Emite warning para campos opcionais ausentes', () => {
    const v = validateSnapshot({ v0: 40, s1: 1, s3: 1, terrainCategory: 'II' });
    expect(v.warnings.length).toBeGreaterThan(0);
  });
});

describe('M9.7 — applySavedProject', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('Aplica wind.v0 corretamente', () => {
    const project: SavedProject = {
      name: 'Teste',
      module: 'galpao',
      inputs: { wind: { v0: 50 } },
      createdAt: Date.now(),
      updatedAt: Date.now(),
    };
    const result = applySavedProject(project);
    expect(result.ok).toBe(true);
    expect(result.appliedFields).toContain('wind.v0');
  });

  it('Aplica múltiplos campos do windStore', () => {
    const project: SavedProject = {
      name: 'Teste',
      module: 'galpao',
      inputs: {
        wind: {
          v0: 45,
          s1: 1.1,
          terrainCategory: 'III',
          s3Group: 2,
          largestDimension: 50,
          heightZ: 20,
        },
      },
      createdAt: Date.now(),
      updatedAt: Date.now(),
    };
    const result = applySavedProject(project);
    expect(result.appliedFields).toContain('wind.v0');
    expect(result.appliedFields).toContain('wind.s1');
    expect(result.appliedFields).toContain('wind.terrainCategory');
    expect(result.appliedFields).toContain('wind.s3Group');
    expect(result.appliedFields ?? []).toContain('wind.dimensions');
  });

  it('Aplica galpaoStore quando module=galpao', () => {
    const project: SavedProject = {
      name: 'Galpão',
      module: 'galpao',
      inputs: {
        galpao: {
          width: 20,
          length: 40,
          height: 8,
          roofPitch: 15,
          windAngle: 90,
          permeabilityCase: 'four-equally-permeable',
          cpiRatio: 0.5,
        },
      },
      createdAt: Date.now(),
      updatedAt: Date.now(),
    };
    const result = applySavedProject(project);
    expect(result.appliedFields).toContain('galpao.width');
    expect(result.appliedFields).toContain('galpao.length');
    expect(result.appliedFields).toContain('galpao.height');
    expect(result.appliedFields).toContain('galpao.roofPitch');
    expect(result.appliedFields).toContain('wind.windAngle');
    expect(result.appliedFields).toContain('wind.permeabilityCase');
    expect(result.appliedFields).toContain('wind.cpiRatio');
  });

  it('Não aplica galpaoStore quando module ≠ galpao', () => {
    const project: SavedProject = {
      name: 'Cilindro',
      module: 'cilindro',
      inputs: { galpao: { width: 20 } },
      createdAt: Date.now(),
      updatedAt: Date.now(),
    };
    const result = applySavedProject(project);
    expect((result.appliedFields ?? []).some((f) => f.startsWith('galpao.'))).toBe(false);
  });

  it('Adiciona warning para categoria inválida', () => {
    const project: SavedProject = {
      name: 'Teste',
      module: 'galpao',
      inputs: { wind: { terrainCategory: 'INVALID' } },
      createdAt: Date.now(),
      updatedAt: Date.now(),
    };
    const result = applySavedProject(project);
    expect(result.warnings?.some((w) => w.includes('Categoria'))).toBe(true);
  });
});

describe('M9.7 — applySnapshot', () => {
  it('Aplica campos básicos', () => {
    const snap = { v0: 50, s1: 1.2, s3: 1.05, terrainCategory: 'IV' };
    const result = applySnapshot(snap);
    expect(result.ok).toBe(true);
    expect(result.appliedFields).toContain('v0');
    expect(result.appliedFields).toContain('s1');
    expect(result.appliedFields).toContain('s3');
    expect(result.appliedFields).toContain('terrainCategory');
  });
});

describe('M9.7 — importProjectFromText (orquestrador)', () => {
  it('Roundtrip: export → import preserva campos principais', () => {
    const project: SavedProject = {
      name: 'Roundtrip',
      module: 'galpao',
      inputs: {
        wind: { v0: 45, s1: 1, s3Group: 2 },
        galpao: { width: 18, length: 35, height: 7 },
      },
      createdAt: Date.now(),
      updatedAt: Date.now(),
    };
    const json = exportProjectToJson(project);
    const result = importProjectFromText(json);
    expect(result.ok).toBe(true);
    expect(result.module).toBe('galpao');
    expect(result.projectName).toBe('Roundtrip');
    expect(result.appliedFields).toContain('wind.v0');
    expect(result.appliedFields).toContain('galpao.width');
  });

  it('Importa snapshot do windStore', () => {
    const json = JSON.stringify({ v0: 50, s1: 1, s3: 1.05, terrainCategory: 'III' });
    const result = importProjectFromText(json);
    expect(result.ok).toBe(true);
    expect(result.appliedFields).toContain('v0');
    expect(result.appliedFields).toContain('terrainCategory');
  });

  it('Retorna erro para JSON malformado', () => {
    const result = importProjectFromText('{invalido}');
    expect(result.ok).toBe(false);
    expect(result.error).toContain('JSON');
  });

  it('Retorna erro para formato desconhecido', () => {
    const result = importProjectFromText('{"foo": "bar"}');
    expect(result.ok).toBe(false);
    expect(result.error).toContain('Formato');
  });

  it('Retorna erro para SavedProject inválido', () => {
    const result = importProjectFromText('{"module": "galpao"}');
    expect(result.ok).toBe(false);
  });
});

describe('M9.7 — snapshotWindStoreToJson', () => {
  it('Exporta JSON válido com campos esperados', () => {
    const json = snapshotWindStoreToJson();
    expect(() => JSON.parse(json)).not.toThrow();
    const parsed = JSON.parse(json) as Record<string, unknown>;
    expect(parsed).toHaveProperty('v0');
    expect(parsed).toHaveProperty('s1');
    expect(parsed).toHaveProperty('s3');
    expect(parsed).toHaveProperty('terrainCategory');
    expect(parsed).toHaveProperty('s2');
    expect(parsed).toHaveProperty('vk');
    expect(parsed).toHaveProperty('q');
  });
});