import { getDatabase } from '../index';
import { Step, StepType, FiringFields, GlazingFields, FormingFields } from '../../../types';
import { generateUUID } from '../../utils/uuid';
import { now } from '../../utils/datetime';

interface CreateStepParams {
  projectId: string;
  type: StepType;
  notesMarkdown?: string;
  photoUris?: string[];
  firing?: FiringFields;
  glazing?: GlazingFields;
  forming?: FormingFields;
}

export async function createStep(params: CreateStepParams): Promise<Step> {
  const db = getDatabase();
  const stepId = generateUUID();
  const timestamp = now();

  // Insert base step
  await db.runAsync(
    `INSERT INTO steps (id, project_id, type, notes_markdown, photo_uris, created_at, updated_at)
     VALUES (?, ?, ?, ?, ?, ?, ?)`,
    [
      stepId,
      params.projectId,
      params.type,
      params.notesMarkdown || null,
      JSON.stringify(params.photoUris || []),
      timestamp,
      timestamp,
    ]
  );

  // Insert type-specific fields
  if (params.type === 'forming' && params.forming) {
    await db.runAsync(
      `INSERT INTO forming_fields (step_id, clay_body, clay_weight_value, clay_weight_unit, production_method, dimensions)
       VALUES (?, ?, ?, ?, ?, ?)`,
      [
        stepId,
        params.forming.clayBody || null,
        params.forming.clayWeight?.value || null,
        params.forming.clayWeight?.unit || null,
        params.forming.productionMethod || null,
        params.forming.dimensions ? JSON.stringify(params.forming.dimensions) : null,
      ]
    );
  } else if (params.type === 'bisque_firing' || params.type === 'glaze_firing') {
    if (params.firing) {
      await db.runAsync(
        `INSERT INTO firing_fields (step_id, cone, temperature_value, temperature_unit, duration_minutes, kiln_notes, kiln_type, kiln_custom_name, kiln_position, kiln_position_notes, firing_schedule)
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
        [
          stepId,
          params.firing.cone || null,
          params.firing.temperature?.value || null,
          params.firing.temperature?.unit || null,
          params.firing.durationMinutes || null,
          params.firing.kilnNotes || null,
          params.firing.kilnType || null,
          params.firing.kilnCustomName || null,
          params.firing.kilnPosition || null,
          params.firing.kilnPositionNotes || null,
          params.firing.firingSchedule || null,
        ]
      );
    }
  } else if (params.type === 'glazing') {
    if (params.glazing) {
      await db.runAsync(
        `INSERT INTO glazing_fields (step_id, glaze_ids, coats, application, mix_notes, glaze_layers)
         VALUES (?, ?, ?, ?, ?, ?)`,
        [
          stepId,
          JSON.stringify(params.glazing.glazeIds || []),
          params.glazing.coats || null,
          params.glazing.application || null,
          params.glazing.mixNotes || null,
          JSON.stringify(params.glazing.glazeLayers),
        ]
      );
    }
  }

  // Update project's updated_at
  await db.runAsync(
    'UPDATE projects SET updated_at = ? WHERE id = ?',
    [timestamp, params.projectId]
  );

  const step = await getStep(stepId);
  if (!step) throw new Error('Failed to create step');
  return step;
}

export async function getStep(id: string): Promise<Step | null> {
  const db = getDatabase();

  const stepRow = await db.getFirstAsync<any>(
    'SELECT * FROM steps WHERE id = ?',
    [id]
  );

  if (!stepRow) return null;

  const baseStep = {
    id: stepRow.id,
    projectId: stepRow.project_id,
    type: stepRow.type as StepType,
    notesMarkdown: stepRow.notes_markdown || undefined,
    photoUris: JSON.parse(stepRow.photo_uris),
    createdAt: stepRow.created_at,
    updatedAt: stepRow.updated_at,
  };

  // Fetch type-specific fields
  if (stepRow.type === 'forming') {
    const formingRow = await db.getFirstAsync<any>(
      'SELECT * FROM forming_fields WHERE step_id = ?',
      [id]
    );

    const forming: FormingFields = {
      clayBody: formingRow?.clay_body || undefined,
      clayWeight: formingRow?.clay_weight_value
        ? { value: formingRow.clay_weight_value, unit: formingRow.clay_weight_unit }
        : undefined,
      productionMethod: formingRow?.production_method || undefined,
      dimensions: formingRow?.dimensions ? JSON.parse(formingRow.dimensions) : undefined,
    };

    return { ...baseStep, type: 'forming', forming } as Step;
  } else if (stepRow.type === 'bisque_firing' || stepRow.type === 'glaze_firing') {
    const firingRow = await db.getFirstAsync<any>(
      'SELECT * FROM firing_fields WHERE step_id = ?',
      [id]
    );

    const firing: FiringFields = {
      cone: firingRow?.cone || undefined,
      temperature: firingRow?.temperature_value
        ? { value: firingRow.temperature_value, unit: firingRow.temperature_unit }
        : undefined,
      durationMinutes: firingRow?.duration_minutes || undefined,
      kilnNotes: firingRow?.kiln_notes || undefined,
      kilnType: firingRow?.kiln_type || undefined,
      kilnCustomName: firingRow?.kiln_custom_name || undefined,
      kilnPosition: firingRow?.kiln_position || undefined,
      kilnPositionNotes: firingRow?.kiln_position_notes || undefined,
      firingSchedule: firingRow?.firing_schedule || undefined,
    };

    return { ...baseStep, type: stepRow.type, firing } as Step;
  } else if (stepRow.type === 'glazing') {
    const glazingRow = await db.getFirstAsync<any>(
      'SELECT * FROM glazing_fields WHERE step_id = ?',
      [id]
    );

    let glazeLayers = [];

    if (glazingRow?.glaze_layers) {
      // New format
      glazeLayers = JSON.parse(glazingRow.glaze_layers);
    } else if (glazingRow?.glaze_ids) {
      // OLD FORMAT - Migrate on read
      const oldGlazeIds = JSON.parse(glazingRow.glaze_ids);
      glazeLayers = oldGlazeIds.map((glazeId: string) => ({
        glazeId,
        application: glazingRow.application || 'brush',
        position: 'entire',
        coats: glazingRow.coats || 2,
        notes: '',
      }));
    }

    const glazing: GlazingFields = {
      glazeIds: glazingRow ? JSON.parse(glazingRow.glaze_ids || '[]') : [],
      glazeLayers,
      coats: glazingRow?.coats || undefined,
      application: glazingRow?.application || undefined,
      mixNotes: glazingRow?.mix_notes || undefined,
    };

    return { ...baseStep, type: 'glazing', glazing } as Step;
  }

  return baseStep as Step;
}

export async function getStepsByProject(projectId: string): Promise<Step[]> {
  const db = getDatabase();
  const rows = await db.getAllAsync<any>(
    'SELECT id FROM steps WHERE project_id = ? ORDER BY created_at ASC',
    [projectId]
  );

  const steps: Step[] = [];
  for (const row of rows) {
    const step = await getStep(row.id);
    if (step) steps.push(step);
  }

  return steps;
}

export async function updateStep(id: string, updates: Partial<CreateStepParams>): Promise<void> {
  const db = getDatabase();
  const step = await getStep(id);
  if (!step) throw new Error('Step not found');

  const timestamp = now();

  // Update base fields
  if (updates.notesMarkdown !== undefined || updates.photoUris !== undefined) {
    await db.runAsync(
      `UPDATE steps
       SET notes_markdown = ?, photo_uris = ?, updated_at = ?
       WHERE id = ?`,
      [
        updates.notesMarkdown !== undefined ? updates.notesMarkdown : step.notesMarkdown || null,
        updates.photoUris !== undefined ? JSON.stringify(updates.photoUris) : JSON.stringify(step.photoUris),
        timestamp,
        id,
      ]
    );
  }

  // Update type-specific fields
  if (step.type === 'forming' && updates.forming) {
    const existingForming = 'forming' in step ? step.forming : {};
    const mergedForming = { ...existingForming, ...updates.forming };

    await db.runAsync(
      `UPDATE forming_fields
       SET clay_body = ?, clay_weight_value = ?, clay_weight_unit = ?, production_method = ?, dimensions = ?
       WHERE step_id = ?`,
      [
        mergedForming.clayBody || null,
        mergedForming.clayWeight?.value || null,
        mergedForming.clayWeight?.unit || null,
        mergedForming.productionMethod || null,
        mergedForming.dimensions ? JSON.stringify(mergedForming.dimensions) : null,
        id,
      ]
    );
  } else if (step.type === 'bisque_firing' || step.type === 'glaze_firing') {
    if (updates.firing) {
      const existingFiring = 'firing' in step ? step.firing : {};
      const mergedFiring = { ...existingFiring, ...updates.firing };

      await db.runAsync(
        `UPDATE firing_fields
         SET cone = ?, temperature_value = ?, temperature_unit = ?, duration_minutes = ?, kiln_notes = ?, kiln_type = ?, kiln_custom_name = ?, kiln_position = ?, kiln_position_notes = ?, firing_schedule = ?
         WHERE step_id = ?`,
        [
          mergedFiring.cone || null,
          mergedFiring.temperature?.value || null,
          mergedFiring.temperature?.unit || null,
          mergedFiring.durationMinutes || null,
          mergedFiring.kilnNotes || null,
          mergedFiring.kilnType || null,
          mergedFiring.kilnCustomName || null,
          mergedFiring.kilnPosition || null,
          mergedFiring.kilnPositionNotes || null,
          mergedFiring.firingSchedule || null,
          id,
        ]
      );
    }
  } else if (step.type === 'glazing' && updates.glazing) {
    const existingGlazing = 'glazing' in step ? step.glazing : { glazeIds: [], glazeLayers: [] };
    const mergedGlazing = { ...existingGlazing, ...updates.glazing };

    await db.runAsync(
      `UPDATE glazing_fields
       SET glaze_ids = ?, coats = ?, application = ?, mix_notes = ?, glaze_layers = ?
       WHERE step_id = ?`,
      [
        JSON.stringify(mergedGlazing.glazeIds || []),
        mergedGlazing.coats || null,
        mergedGlazing.application || null,
        mergedGlazing.mixNotes || null,
        JSON.stringify(mergedGlazing.glazeLayers),
        id,
      ]
    );
  }

  // Update project's updated_at
  await db.runAsync(
    'UPDATE projects SET updated_at = ? WHERE id = ?',
    [timestamp, step.projectId]
  );
}

export async function deleteStep(id: string): Promise<void> {
  const db = getDatabase();
  const step = await getStep(id);
  if (!step) return;

  await db.runAsync('DELETE FROM steps WHERE id = ?', [id]);

  // Update project's updated_at
  await db.runAsync(
    'UPDATE projects SET updated_at = ? WHERE id = ?',
    [now(), step.projectId]
  );
}
