import { protectedProcedure, TRPCError } from './router';
import { z } from 'zod';
import { eq, and, like, sql, inArray } from 'drizzle-orm';
import type { DrizzleDB } from '../../src/db/config/migrations';
import {
  scripts,
  revisions,
  revisionChanges,
  projects,
  projectMembers,
} from '../../src/db/schema';

function slugify(title: string): string {
  return title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
}

// H2 fix: verifies user has access to a project (owner or member)
async function verifyProjectAccess(
  db: DrizzleDB,
  projectId: number,
  userId: number
) {
  const projectRows = await db
    .select({ id: projects.id, ownerId: projects.ownerId })
    .from(projects)
    .where(eq(projects.id, projectId));

  const project = projectRows[0];
  if (!project) {
    throw new TRPCError({ code: 'NOT_FOUND', message: `Project ${projectId} not found` });
  }

  if (project.ownerId === userId) return project;

  const memberRows = await db
    .select()
    .from(projectMembers)
    .where(and(eq(projectMembers.projectId, projectId), eq(projectMembers.userId, userId)));

  if (memberRows.length === 0) {
    throw new TRPCError({ code: 'FORBIDDEN', message: `You do not have access to project ${projectId}` });
  }

  return project;
}

async function verifyScriptOwnership(
  db: DrizzleDB,
  scriptId: number,
  userId: number
) {
  const scriptRows = await db.select({ id: scripts.id, projectId: scripts.projectId })
    .from(scripts)
    .where(eq(scripts.id, scriptId));

  const script = scriptRows[0];
  if (!script) {
    throw new TRPCError({ code: 'NOT_FOUND', message: `Script ${scriptId} not found` });
  }

  await verifyProjectAccess(db, script.projectId, userId);

  return script;
}

export const scriptsRouter = {
  listScripts: protectedProcedure
    .input(z.object({ projectId: z.number().int().positive() }))
    .query(async ({ input, ctx }) => {
      await verifyProjectAccess(ctx.db!, input.projectId, ctx.userId!);

      return await ctx.db!.select()
        .from(scripts)
        .where(eq(scripts.projectId, input.projectId))
        .orderBy(scripts.updatedAt);
    }),

  getScript: protectedProcedure
    .input(z.object({ id: z.number().int().positive() }))
    .query(async ({ input, ctx }) => {
      await verifyScriptOwnership(ctx.db!, input.id, ctx.userId!);
      const rows = await ctx.db!.select()
        .from(scripts)
        .where(eq(scripts.id, input.id));
      const script = rows[0];
      if (!script) {
        throw new TRPCError({ code: 'NOT_FOUND', message: `Script ${input.id} not found` });
      }
      return script;
    }),

  createScript: protectedProcedure
    .input(z.object({
      title: z.string().min(1).max(255),
      projectId: z.number().int().positive(),
      genre: z.string().optional(),
      logline: z.string().optional(),
      status: z.enum(['draft', 'revision', 'final', 'published']).optional(),
    }))
    .mutation(async ({ input, ctx }) => {
      await verifyProjectAccess(ctx.db!, input.projectId, ctx.userId!);
      
      const result = await ctx.db!.insert(scripts)
        .values({
          title: input.title,
          slug: slugify(input.title),
          projectId: input.projectId,
          genre: input.genre ?? null,
          logline: input.logline ?? null,
          status: input.status ?? 'draft',
        })
        .returning();
      return result[0];
    }),

  updateScript: protectedProcedure
    .input(z.object({
      id: z.number().int().positive(),
      title: z.string().min(1).max(255).optional(),
      genre: z.string().optional(),
      logline: z.string().optional(),
      status: z.enum(['draft', 'revision', 'final', 'published']).optional(),
    }))
    .mutation(async ({ input, ctx }) => {
      await verifyScriptOwnership(ctx.db!, input.id, ctx.userId!);

      const updateData: Record<string, any> = { updatedAt: new Date() };
      if (input.title !== undefined) {
        updateData.title = input.title;
        updateData.slug = slugify(input.title);
      }
      if (input.genre !== undefined) updateData.genre = input.genre ?? null;
      if (input.logline !== undefined) updateData.logline = input.logline ?? null;
      if (input.status !== undefined) updateData.status = input.status;

      const result = await ctx.db!.update(scripts)
        .set(updateData)
        .where(eq(scripts.id, input.id))
        .returning();
      return result[0];
    }),

  deleteScript: protectedProcedure
    .input(z.object({ id: z.number().int().positive() }))
    .mutation(async ({ input, ctx }) => {
      await verifyScriptOwnership(ctx.db!, input.id, ctx.userId!);

      // Get revision IDs for this script
      const scriptRevisions = await ctx.db!.select({ id: revisions.id })
        .from(revisions)
        .where(eq(revisions.scriptId, input.id));

      // Delete revision changes for each revision
      for (const rev of scriptRevisions) {
        await ctx.db!.delete(revisionChanges)
          .where(eq(revisionChanges.revisionId, rev.id));
      }

      // Delete revisions
      await ctx.db!.delete(revisions)
        .where(eq(revisions.scriptId, input.id));

      // Delete script
      await ctx.db!.delete(scripts)
        .where(eq(scripts.id, input.id));

      return { success: true };
    }),

  searchScripts: protectedProcedure
    .input(z.object({
      projectId: z.number().int().positive(),
      query: z.string().optional(),
    }))
    .query(async ({ input, ctx }) => {
      await verifyProjectAccess(ctx.db!, input.projectId, ctx.userId!);

      const conditions: any[] = [eq(scripts.projectId, input.projectId)];

      if (input.query) {
        const q = `%${input.query.toLowerCase()}%`;
        conditions.push(
          like(sql`LOWER(${scripts.title})`, q),
        );
      }

      return await ctx.db!.select()
        .from(scripts)
        .where(and(...conditions))
        .orderBy(scripts.title);
    }),
};
