/**
 * Mutaciones de alto nivel. Cada una:
 *   1. (updates) escribe optimistamente en la BD local para reflejar el cambio ya,
 *   2. encola la operación en el outbox para enviarla cuando haya red.
 *
 * El servidor SIEMPRE fija user_id/reported_by/project_id y valida permisos:
 * el cliente nunca los envía (ver brief §5.1).
 */
import { getDb } from '../db/database';
import { enqueueOperation } from '../db/outbox';
import { insertLocalCreate } from '../db/repositories';
import { newUuid, nextTempId, nowIso } from './uuid';
import {
  IssuePriority,
  IssueStatus,
  IssueType,
} from '../api/types';

// ----- progress (append-only en servidor) -----

export function recordProgressUpdate(input: {
  phase_id: number;
  progress: number;
  comment?: string;
  location?: { lat: number; lng: number };
}): Promise<string> {
  return enqueueOperation('progress_update', 'create', { ...input });
}

// ----- feature.update (editable, last-write-wins) -----

export async function updateFeature(input: {
  id: number;
  status?: string;
  progress?: number;
  responsible?: string;
}): Promise<string> {
  const db = await getDb();
  const sets: string[] = [];
  const params: unknown[] = [];
  if (input.status !== undefined) {
    sets.push('status = ?');
    params.push(input.status);
  }
  if (input.progress !== undefined) {
    sets.push('progress = ?');
    params.push(input.progress);
  }
  if (input.responsible !== undefined) {
    sets.push('responsible = ?');
    params.push(input.responsible);
  }
  if (sets.length) {
    params.push(input.id);
    await db.runAsync(
      `UPDATE features SET ${sets.join(', ')}, dirty = 1 WHERE id = ?`,
      ...(params as never[]),
    );
  }
  return enqueueOperation('feature', 'update', { ...input });
}

// ----- inspection.create (append-only) -----

export async function createInspection(input: {
  feature_id: number;
  template_id?: number;
  data?: Record<string, unknown>;
  status?: string;
  result?: string;
  notes?: string;
}): Promise<string> {
  const uuid = newUuid();
  const tempId = nextTempId();
  await insertLocalCreate('inspection', tempId, uuid, {
    feature_id: input.feature_id,
    template_id: input.template_id ?? null,
    data: input.data ? JSON.stringify(input.data) : null,
    status: input.status ?? null,
    result: input.result ?? null,
    notes: input.notes ?? null,
    created_at: nowIso(),
    updated_at: nowIso(),
    dirty: 1,
  });
  return enqueueOperation('inspection', 'create', { ...input }, { uuid, localId: tempId });
}

// ----- issues -----

export async function createIssue(input: {
  project_id: number;
  feature_id?: number;
  title: string;
  description?: string;
  priority?: IssuePriority;
  status?: IssueStatus;
  type?: IssueType;
}): Promise<string> {
  const uuid = newUuid();
  const tempId = nextTempId();
  await insertLocalCreate('issue', tempId, uuid, {
    project_id: input.project_id,
    feature_id: input.feature_id ?? null,
    title: input.title,
    description: input.description ?? null,
    priority: input.priority ?? null,
    status: input.status ?? 'open',
    type: input.type ?? null,
    updated_at: nowIso(),
    dirty: 1,
  });
  return enqueueOperation('issue', 'create', { ...input }, { uuid, localId: tempId });
}

export async function updateIssue(input: {
  id: number;
  title?: string;
  description?: string;
  priority?: IssuePriority;
  status?: IssueStatus;
  type?: IssueType;
  assigned_to?: number;
  resolution_notes?: string;
}): Promise<string> {
  const db = await getDb();
  const cols: Record<string, unknown> = {
    title: input.title,
    description: input.description,
    priority: input.priority,
    status: input.status,
    type: input.type,
    assigned_to: input.assigned_to,
    resolution_notes: input.resolution_notes,
  };
  const sets: string[] = [];
  const params: unknown[] = [];
  for (const [k, v] of Object.entries(cols)) {
    if (v !== undefined) {
      sets.push(`${k} = ?`);
      params.push(v);
    }
  }
  if (sets.length) {
    params.push(input.id);
    await db.runAsync(
      `UPDATE issues SET ${sets.join(', ')}, dirty = 1 WHERE id = ?`,
      ...(params as never[]),
    );
  }
  return enqueueOperation('issue', 'update', { ...input });
}

// ----- issue tasks -----

export async function createIssueTask(input: {
  issue_id: number;
  title: string;
  assigned_to?: number;
  due_date?: string;
  is_done?: boolean;
}): Promise<string> {
  const uuid = newUuid();
  const tempId = nextTempId();
  await insertLocalCreate('issue_task', tempId, uuid, {
    issue_id: input.issue_id,
    title: input.title,
    is_done: input.is_done ? 1 : 0,
    assigned_to: input.assigned_to ?? null,
    due_date: input.due_date ?? null,
    updated_at: nowIso(),
    dirty: 1,
  });
  return enqueueOperation('issue_task', 'create', { ...input }, { uuid, localId: tempId });
}

export async function updateIssueTask(input: {
  id: number;
  title?: string;
  assigned_to?: number;
  due_date?: string;
  is_done?: boolean;
}): Promise<string> {
  const db = await getDb();
  if (input.is_done !== undefined) {
    await db.runAsync(
      'UPDATE issue_tasks SET is_done = ?, dirty = 1 WHERE id = ?',
      input.is_done ? 1 : 0,
      input.id,
    );
  }
  return enqueueOperation('issue_task', 'update', { ...input });
}

// ----- issue comments (append-only) -----

export async function createIssueComment(input: {
  issue_id: number;
  body: string;
}): Promise<string> {
  const uuid = newUuid();
  const tempId = nextTempId();
  await insertLocalCreate('issue_comment', tempId, uuid, {
    issue_id: input.issue_id,
    body: input.body,
    created_at: nowIso(),
    updated_at: nowIso(),
  });
  return enqueueOperation('issue_comment', 'create', { ...input }, { uuid, localId: tempId });
}
