/**
 * Task-dependency CRUD routes — task-dependencies.
 *
 * Mounted under the tasks module router (tasks-module/index.ts)
 * which already applies authMiddleware + requireModuleEnabled('tasks').
 *
 * GET    /api/tasks/:id/dependencies                 — list blocking/blocked_by
 * POST   /api/tasks/:id/dependencies                 — add a dependency edge
 * DELETE /api/tasks/:id/dependencies/:dependencyId   — remove a dependency edge
 *
 * Permissions:
 *   projects:read   — GET
 *   projects:write  — POST, DELETE
 *
 * Tier: Business+ required for all routes.
 */
import { Hono } from 'hono'
import type { AppEnv } from '../types'
import { requirePermission, requireTier } from '../middleware/guards'
import {
  getDependencies,
  insertDependency,
  deleteDependency,
  assertSameProject,
  wouldCreateCycle,
  bumpProjectRevision,
  DuplicateDependencyError,
  CrossProjectDependencyError,
  getTask,
} from '@zync/db/queries'
import {
  createDependencySchema,
  dependencyIdParamSchema,
} from '../schemas/task-dependencies'
import { TenantTier } from '@zync/types'

export const taskDependenciesRoute = new Hono<AppEnv>()

// All dependency routes require Business+ tier
taskDependenciesRoute.use('*', requireTier(TenantTier.BUSINESS))

// ── GET /api/tasks/:id/dependencies ──────────────────────────────────────────

taskDependenciesRoute.get(
  '/:id/dependencies',
  requirePermission('projects:read'),
  async (c) => {
    const session = c.get('session')
    if (!session || session.type !== 'user' || !session.tid) {
      return c.json({ error: 'Unauthorized' }, 401)
    }

    const taskId = c.req.param('id')
    const db = c.get('db')

    const result = await getDependencies(db, session.tid, taskId)
    return c.json(result, 200)
  },
)

// ── POST /api/tasks/:id/dependencies ─────────────────────────────────────────

taskDependenciesRoute.post(
  '/:id/dependencies',
  requirePermission('projects:write'),
  async (c) => {
    const session = c.get('session')
    if (!session || session.type !== 'user' || !session.tid) {
      return c.json({ error: 'Unauthorized' }, 401)
    }

    const taskId = c.req.param('id')
    const body = await c.req.json().catch(() => null)
    const parsed = createDependencySchema.safeParse(body)
    if (!parsed.success) {
      return c.json({ error: 'Invalid request', issues: parsed.error.issues }, 422)
    }

    // Resolve edge direction:
    //   blocking_task_id supplied → that task blocks :id  (edge: supplied → :id)
    //   blocked_task_id  supplied → :id blocks that task  (edge: :id → supplied)
    const { blocking_task_id, blocked_task_id } = parsed.data
    const blockingTaskId = blocking_task_id ?? taskId
    const blockedTaskId = blocked_task_id ?? taskId

    const db = c.get('db')
    const tenantId = session.tid

    // Cross-project guard
    let projectId: string
    try {
      const result = await assertSameProject(db, tenantId, blockingTaskId, blockedTaskId)
      projectId = result.projectId
    } catch (err) {
      if (err instanceof CrossProjectDependencyError) {
        return c.json({ error: 'cross_project_dependency', message: err.message }, 422)
      }
      throw err
    }

    // Cycle detection
    const hasCycle = await wouldCreateCycle(db, tenantId, {
      projectId,
      blockingTaskId,
      blockedTaskId,
    })
    if (hasCycle) {
      return c.json({ error: 'cycle_detected', message: 'Adding this dependency would create a cycle' }, 409)
    }

    // Insert
    let dep
    try {
      dep = await insertDependency(db, tenantId, {
        blockingTaskId,
        blockedTaskId,
        createdBy: session.sub,
      })
    } catch (err) {
      if (err instanceof DuplicateDependencyError) {
        return c.json({ error: 'duplicate_dependency', message: err.message }, 409)
      }
      throw err
    }

    // Bump project revision so critical-path cache invalidates
    await bumpProjectRevision(c.env, tenantId, projectId)

    return c.json(
      {
        id: dep.id,
        blocking_task_id: dep.blocking_task_id,
        blocked_task_id: dep.blocked_task_id,
      },
      201,
    )
  },
)

// ── DELETE /api/tasks/:id/dependencies/:dependencyId ─────────────────────────

taskDependenciesRoute.delete(
  '/:id/dependencies/:dependencyId',
  requirePermission('projects:write'),
  async (c) => {
    const session = c.get('session')
    if (!session || session.type !== 'user' || !session.tid) {
      return c.json({ error: 'Unauthorized' }, 401)
    }

    const paramParsed = dependencyIdParamSchema.safeParse({
      dependencyId: c.req.param('dependencyId'),
    })
    if (!paramParsed.success) {
      return c.json({ error: 'Invalid dependencyId', issues: paramParsed.error.issues }, 422)
    }

    const db = c.get('db')
    const tenantId = session.tid
    const task = await getTask(db, tenantId, c.req.param('id'))

    await deleteDependency(db, tenantId, paramParsed.data.dependencyId)
    if (task?.project_id) {
      await bumpProjectRevision(c.env, tenantId, task.project_id)
    }

    return c.body(null, 204)
  },
)
