/**
 * Change Set Router — list, get, revert workflows
 */

import { TRPCError } from '@trpc/server';
import { z } from 'zod';
import type { ProcedureBuilder } from '../trpc.js';
import {
  requireChangeSetRepository,
  requirePhaseRepository,
  requireTaskRepository,
  requirePageRepository,
} from './_helpers.js';

export function changeSetProcedures(publicProcedure: ProcedureBuilder) {
  return {
    listChangeSets: publicProcedure
      .input(z.object({
        initiativeId: z.string().min(1).optional(),
        agentId: z.string().min(1).optional(),
      }))
      .query(async ({ ctx, input }) => {
        const repo = requireChangeSetRepository(ctx);
        if (input.agentId) {
          return repo.findByAgentId(input.agentId);
        }
        if (input.initiativeId) {
          return repo.findByInitiativeId(input.initiativeId);
        }
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'Either agentId or initiativeId is required',
        });
      }),

    getChangeSet: publicProcedure
      .input(z.object({ id: z.string().min(1) }))
      .query(async ({ ctx, input }) => {
        const repo = requireChangeSetRepository(ctx);
        const cs = await repo.findByIdWithEntries(input.id);
        if (!cs) {
          throw new TRPCError({ code: 'NOT_FOUND', message: `ChangeSet '${input.id}' not found` });
        }
        return cs;
      }),

    revertChangeSet: publicProcedure
      .input(z.object({ id: z.string().min(1), force: z.boolean().optional() }))
      .mutation(async ({ ctx, input }) => {
        const repo = requireChangeSetRepository(ctx);
        const cs = await repo.findByIdWithEntries(input.id);
        if (!cs) {
          throw new TRPCError({ code: 'NOT_FOUND', message: `ChangeSet '${input.id}' not found` });
        }
        if (cs.status === 'reverted') {
          throw new TRPCError({ code: 'BAD_REQUEST', message: 'ChangeSet is already reverted' });
        }

        const phaseRepo = requirePhaseRepository(ctx);
        const taskRepo = requireTaskRepository(ctx);
        const pageRepo = requirePageRepository(ctx);

        // Conflict detection (unless force)
        if (!input.force) {
          const conflicts: string[] = [];
          for (const entry of cs.entries) {
            if (entry.action === 'create') {
              if (entry.entityType === 'phase') {
                const phase = await phaseRepo.findById(entry.entityId);
                if (phase && phase.status === 'in_progress') {
                  conflicts.push(`Phase "${phase.name}" is in progress`);
                }
              } else if (entry.entityType === 'task') {
                const task = await taskRepo.findById(entry.entityId);
                if (task && task.status === 'in_progress') {
                  conflicts.push(`Task "${task.name}" is in progress`);
                }
              }
            } else if (entry.action === 'update' && entry.entityType === 'page' && entry.newState) {
              const page = await pageRepo.findById(entry.entityId);
              if (page) {
                const expectedContent = JSON.parse(entry.newState).content;
                if (page.content !== expectedContent) {
                  conflicts.push(`Page "${page.title}" was modified since change set was applied`);
                }
              }
            }
          }
          if (conflicts.length > 0) {
            return { success: false as const, conflicts };
          }
        }

        // Mark reverted FIRST to avoid ghost state if entity deletion fails partway
        await repo.markReverted(input.id);

        // Apply reverts in reverse entry order
        const reversedEntries = [...cs.entries].reverse();
        for (const entry of reversedEntries) {
          try {
            if (entry.action === 'create') {
              switch (entry.entityType) {
                case 'phase':
                  try { await phaseRepo.delete(entry.entityId); } catch { /* already deleted */ }
                  break;
                case 'task':
                  try { await taskRepo.delete(entry.entityId); } catch { /* already deleted */ }
                  break;
                case 'phase_dependency': {
                  const depData = JSON.parse(entry.newState || '{}');
                  if (depData.phaseId && depData.dependsOnPhaseId) {
                    try { await phaseRepo.removeDependency(depData.phaseId, depData.dependsOnPhaseId); } catch { /* already removed */ }
                  }
                  break;
                }
              }
            } else if (entry.action === 'update' && entry.previousState) {
              const prev = JSON.parse(entry.previousState);
              switch (entry.entityType) {
                case 'phase':
                  await phaseRepo.update(entry.entityId, {
                    name: prev.name,
                    content: prev.content,
                  });
                  break;
                case 'task':
                  await taskRepo.update(entry.entityId, {
                    name: prev.name,
                    description: prev.description,
                    category: prev.category,
                    type: prev.type,
                  });
                  break;
                case 'page':
                  await pageRepo.update(entry.entityId, {
                    content: prev.content,
                    title: prev.title,
                  });
                  ctx.eventBus.emit({
                    type: 'page:updated',
                    timestamp: new Date(),
                    payload: { pageId: entry.entityId, initiativeId: cs.initiativeId, title: prev.title },
                  });
                  break;
              }
            } else if (entry.action === 'delete' && entry.previousState) {
              const prev = JSON.parse(entry.previousState);
              switch (entry.entityType) {
                case 'phase':
                  try { await phaseRepo.create({ id: prev.id, initiativeId: prev.initiativeId, name: prev.name, content: prev.content }); } catch { /* already exists */ }
                  break;
                case 'task':
                  try { await taskRepo.create({ id: prev.id, initiativeId: prev.initiativeId, phaseId: prev.phaseId, parentTaskId: prev.parentTaskId, name: prev.name, description: prev.description, category: prev.category, type: prev.type }); } catch { /* already exists */ }
                  break;
                case 'page':
                  try { await pageRepo.create({ id: prev.id, initiativeId: prev.initiativeId, parentPageId: prev.parentPageId, title: prev.title, content: prev.content }); } catch { /* already exists */ }
                  break;
              }
            }
          } catch (err) {
            // Log but continue reverting other entries
          }
        }

        ctx.eventBus.emit({
          type: 'changeset:reverted' as const,
          timestamp: new Date(),
          payload: { changeSetId: cs.id, initiativeId: cs.initiativeId },
        });

        return { success: true as const };
      }),
  };
}
