/**
 * Query Routes
 *
 * Graph traversal and entity query endpoints.
 */

import { Hono } from 'hono';
import { zValidator } from '@hono/zod-validator';
import type { AppContext } from '../router.js';
import {
  ExpandRequestSchema,
  type ExpandResponse,
  type CallersResponse,
  type CalleesResponse,
  type HierarchyResponse,
  type EntityResult,
} from '../types.js';
import { GraphQueries } from '../../graph/queries.js';

export const queryRoutes = new Hono<AppContext>();

/**
 * GET /query/entity/:type/:qualifiedName - Get entity by type and name
 */
queryRoutes.get('/entity/:type/:qualifiedName', async (c) => {
  const conn = c.get('conn');
  const { type, qualifiedName } = c.req.param();

  if (!conn) {
    return c.json({ error: 'No database connection' }, 500);
  }

  try {
    const queries = new GraphQueries(conn);
    let entity: EntityResult | null = null;

    switch (type.toLowerCase()) {
      case 'file': {
        const file = await queries.getFile(qualifiedName);
        if (file) {
          entity = {
            qualifiedName: file.path,
            name: file.name,
            entityType: 'File',
            filePath: file.path,
          };
        }
        break;
      }
      case 'class': {
        const cls = await queries.getClass(qualifiedName);
        if (cls) {
          entity = {
            qualifiedName: cls.qualifiedName,
            name: cls.name,
            entityType: 'Class',
            filePath: cls.filePath,
            lineStart: cls.lineStart,
            lineEnd: cls.lineEnd,
            description: cls.docstring,
          };
        }
        break;
      }
      case 'function': {
        const fn = await queries.getFunction(qualifiedName);
        if (fn) {
          entity = {
            qualifiedName: fn.qualifiedName,
            name: fn.name,
            entityType: 'Function',
            filePath: fn.filePath,
            lineStart: fn.lineStart,
            lineEnd: fn.lineEnd,
            description: fn.docstring,
          };
        }
        break;
      }
      case 'module': {
        const mod = await queries.getModule(qualifiedName);
        if (mod) {
          entity = {
            qualifiedName: mod.name,
            name: mod.name,
            entityType: 'Module',
          };
        }
        break;
      }
      default:
        return c.json({ error: `Unknown entity type: ${type}` }, 400);
    }

    if (!entity) {
      return c.json({ error: `Entity not found: ${qualifiedName}` }, 404);
    }

    return c.json(entity);
  } catch (error) {
    return c.json(
      { error: error instanceof Error ? error.message : 'Unknown error' },
      500
    );
  }
});

/**
 * GET /query/callers/:qualifiedName - Get callers of a function
 */
queryRoutes.get('/callers/:qualifiedName', async (c) => {
  const conn = c.get('conn');
  const { qualifiedName } = c.req.param();
  const depth = parseInt(c.req.query('depth') ?? '3', 10);

  if (!conn) {
    return c.json({ error: 'No database connection' }, 500);
  }

  try {
    const queries = new GraphQueries(conn);
    const callerNodes = await queries.getCallers(qualifiedName, depth);

    const callers: EntityResult[] = callerNodes.map((node) => ({
      qualifiedName: node.qualifiedName,
      name: node.name,
      entityType: 'Function',
    }));

    const response: CallersResponse = {
      qualifiedName,
      callers,
      depth,
    };

    return c.json(response);
  } catch (error) {
    return c.json(
      { error: error instanceof Error ? error.message : 'Unknown error' },
      500
    );
  }
});

/**
 * GET /query/callees/:qualifiedName - Get callees of a function
 */
queryRoutes.get('/callees/:qualifiedName', async (c) => {
  const conn = c.get('conn');
  const { qualifiedName } = c.req.param();
  const depth = parseInt(c.req.query('depth') ?? '3', 10);

  if (!conn) {
    return c.json({ error: 'No database connection' }, 500);
  }

  try {
    const queries = new GraphQueries(conn);
    const calleeNodes = await queries.getCallGraph(qualifiedName, depth);

    const callees: EntityResult[] = calleeNodes.map((node) => ({
      qualifiedName: node.qualifiedName,
      name: node.name,
      entityType: 'Function',
    }));

    const response: CalleesResponse = {
      qualifiedName,
      callees,
      depth,
    };

    return c.json(response);
  } catch (error) {
    return c.json(
      { error: error instanceof Error ? error.message : 'Unknown error' },
      500
    );
  }
});

/**
 * GET /query/hierarchy/:qualifiedName - Get class hierarchy
 */
queryRoutes.get('/hierarchy/:qualifiedName', async (c) => {
  const conn = c.get('conn');
  const { qualifiedName } = c.req.param();

  if (!conn) {
    return c.json({ error: 'No database connection' }, 500);
  }

  try {
    const queries = new GraphQueries(conn);
    const hierarchy = await queries.getClassHierarchy(qualifiedName);

    const parents: EntityResult[] = hierarchy.parents.map((node) => ({
      qualifiedName: node.qualifiedName,
      name: node.name,
      entityType: 'Class',
    }));

    const children: EntityResult[] = hierarchy.children.map((node) => ({
      qualifiedName: node.qualifiedName,
      name: node.name,
      entityType: 'Class',
    }));

    const response: HierarchyResponse = {
      qualifiedName,
      parents,
      children,
    };

    return c.json(response);
  } catch (error) {
    return c.json(
      { error: error instanceof Error ? error.message : 'Unknown error' },
      500
    );
  }
});

/**
 * POST /query/expand - Expand entity relationships
 */
queryRoutes.post(
  '/expand',
  zValidator('json', ExpandRequestSchema),
  async (c) => {
    const conn = c.get('conn');
    const body = c.req.valid('json');

    if (!conn) {
      return c.json({ error: 'No database connection' }, 500);
    }

    try {
      const queries = new GraphQueries(conn);
      let entity: EntityResult | null = null;
      let callers: EntityResult[] = [];
      let callees: EntityResult[] = [];
      let dependencies: EntityResult[] = [];
      let dependents: EntityResult[] = [];

      if (body.entityType === 'Function') {
        const fn = await queries.getFunction(body.qualifiedName);
        if (fn) {
          entity = {
            qualifiedName: fn.qualifiedName,
            name: fn.name,
            entityType: 'Function',
            filePath: fn.filePath,
            lineStart: fn.lineStart,
            lineEnd: fn.lineEnd,
            description: fn.docstring,
          };

          // Get callers and callees
          const callerNodes = await queries.getCallers(body.qualifiedName, body.maxHops);
          callers = callerNodes.map((n) => ({
            qualifiedName: n.qualifiedName,
            name: n.name,
            entityType: 'Function' as const,
          }));

          const calleeNodes = await queries.getCallGraph(body.qualifiedName, body.maxHops);
          callees = calleeNodes.map((n) => ({
            qualifiedName: n.qualifiedName,
            name: n.name,
            entityType: 'Function' as const,
          }));
        }
      } else if (body.entityType === 'Class') {
        const cls = await queries.getClass(body.qualifiedName);
        if (cls) {
          entity = {
            qualifiedName: cls.qualifiedName,
            name: cls.name,
            entityType: 'Class',
            filePath: cls.filePath,
            lineStart: cls.lineStart,
            lineEnd: cls.lineEnd,
            description: cls.docstring,
          };

          // Get hierarchy
          const hierarchy = await queries.getClassHierarchy(body.qualifiedName);
          dependencies = hierarchy.parents.map((n) => ({
            qualifiedName: n.qualifiedName,
            name: n.name,
            entityType: 'Class' as const,
          }));
          dependents = hierarchy.children.map((n) => ({
            qualifiedName: n.qualifiedName,
            name: n.name,
            entityType: 'Class' as const,
          }));

          // Get methods as callees
          const methods = await queries.getMethodsOfClass(body.qualifiedName);
          callees = methods.map((m) => ({
            qualifiedName: m.qualifiedName,
            name: m.name,
            entityType: 'Function' as const,
            filePath: m.filePath,
            lineStart: m.lineStart,
            lineEnd: m.lineEnd,
          }));
        }
      } else if (body.entityType === 'File') {
        const file = await queries.getFile(body.qualifiedName);
        if (file) {
          entity = {
            qualifiedName: file.path,
            name: file.name,
            entityType: 'File',
            filePath: file.path,
          };

          // Get imports as dependencies
          const imports = await queries.getImports(body.qualifiedName);
          dependencies = imports.map((m) => ({
            qualifiedName: m.name,
            name: m.name,
            entityType: 'Module' as const,
          }));

          // Get classes and functions in file as callees
          const classes = await queries.getClassesInFile(body.qualifiedName);
          const functions = await queries.getFunctionsInFile(body.qualifiedName);

          callees = [
            ...classes.map((cls) => ({
              qualifiedName: cls.qualifiedName,
              name: cls.name,
              entityType: 'Class' as const,
              filePath: cls.filePath,
              lineStart: cls.lineStart,
              lineEnd: cls.lineEnd,
            })),
            ...functions.map((fn) => ({
              qualifiedName: fn.qualifiedName,
              name: fn.name,
              entityType: 'Function' as const,
              filePath: fn.filePath,
              lineStart: fn.lineStart,
              lineEnd: fn.lineEnd,
            })),
          ];
        }
      }

      if (!entity) {
        return c.json({ error: `Entity not found: ${body.qualifiedName}` }, 404);
      }

      const response: ExpandResponse = {
        entity,
        callers,
        callees,
        dependencies,
        dependents,
      };

      return c.json(response);
    } catch (error) {
      return c.json(
        { error: error instanceof Error ? error.message : 'Unknown error' },
        500
      );
    }
  }
);

/**
 * GET /query/file/:path/contents - Get file contents (classes, functions)
 */
queryRoutes.get('/file/:path/contents', async (c) => {
  const conn = c.get('conn');
  const path = c.req.param('path');

  if (!conn) {
    return c.json({ error: 'No database connection' }, 500);
  }

  try {
    const queries = new GraphQueries(conn);

    const [classes, functions, imports] = await Promise.all([
      queries.getClassesInFile(path),
      queries.getFunctionsInFile(path),
      queries.getImports(path),
    ]);

    return c.json({
      path,
      classes: classes.map((cls) => ({
        qualifiedName: cls.qualifiedName,
        name: cls.name,
        lineStart: cls.lineStart,
        lineEnd: cls.lineEnd,
        description: cls.docstring,
      })),
      functions: functions.map((fn) => ({
        qualifiedName: fn.qualifiedName,
        name: fn.name,
        lineStart: fn.lineStart,
        lineEnd: fn.lineEnd,
        description: fn.docstring,
      })),
      imports: imports.map((m) => ({
        name: m.name,
        importPath: m.importPath,
        isExternal: m.isExternal,
      })),
    });
  } catch (error) {
    return c.json(
      { error: error instanceof Error ? error.message : 'Unknown error' },
      500
    );
  }
});

/**
 * GET /query/stats - Get codebase statistics
 */
queryRoutes.get('/stats', async (c) => {
  const conn = c.get('conn');

  if (!conn) {
    return c.json({ error: 'No database connection' }, 500);
  }

  try {
    const queries = new GraphQueries(conn);
    const stats = await queries.getFileStats();
    const deadCode = await queries.getDeadCode();
    const orphanClasses = await queries.getOrphanClasses();

    return c.json({
      ...stats,
      deadCodeCount: deadCode.length,
      orphanClassCount: orphanClasses.length,
    });
  } catch (error) {
    return c.json(
      { error: error instanceof Error ? error.message : 'Unknown error' },
      500
    );
  }
});
