import express, { Request, Response } from 'express';
import { readdirSync, statSync, readFileSync, mkdirSync } from 'fs';
import { join, resolve, sep, extname } from 'path';
import { Repository, localTaskRepoName } from '../db/repository.js';
import { logger } from '../logger.js';
import { parseTaskId } from './validation.js';
import { checkTaskOwnership } from './local-api-helpers.js';

function ensurePathWithin(baseDir: string, requestedPath: string): string {
  const resolvedBase = resolve(baseDir);
  const resolvedPath = resolve(baseDir, requestedPath);
  if (!resolvedPath.startsWith(resolvedBase + sep) && resolvedPath !== resolvedBase) {
    throw new Error('Path escapes workspace');
  }
  return resolvedPath;
}

function sanitizeTaskForPublic(task: Record<string, unknown>): Record<string, unknown> {
  const { ownerId, workspacePath, body, ...safe } = task;
  return safe;
}

export function mountShareApi(app: express.Application, repo: Repository): void {
  // ── 公開エンドポイント（認証不要） ──

  app.get('/api/shared/:token', async (req: Request, res: Response) => {
    try {
      const task = await repo.getLocalTaskByShareToken(req.params.token);
      if (!task) { res.status(404).json({ error: 'Not found' }); return; }
      res.json({ task: sanitizeTaskForPublic(task as unknown as Record<string, unknown>) });
    } catch (err) {
      logger.error(`Shared task API error: ${err}`);
      res.status(500).json({ error: 'Failed to fetch shared task' });
    }
  });

  app.get('/api/shared/:token/comments', async (req: Request, res: Response) => {
    try {
      const task = await repo.getLocalTaskByShareToken(req.params.token);
      if (!task) { res.status(404).json({ error: 'Not found' }); return; }
      const comments = await repo.listLocalTaskComments(task.id);
      res.json({ comments });
    } catch (err) {
      logger.error(`Shared comments API error: ${err}`);
      res.status(500).json({ error: 'Failed to fetch comments' });
    }
  });

  app.get('/api/shared/:token/files', async (req: Request, res: Response) => {
    try {
      const task = await repo.getLocalTaskByShareToken(req.params.token);
      if (!task || !task.workspacePath) { res.status(404).json({ error: 'Not found' }); return; }

      const relativeDir = String(req.query.path ?? '').replace(/^\/+/, '').replace(/\/+$/, '');
      const rootDir = join(task.workspacePath, 'output');
      mkdirSync(rootDir, { recursive: true });
      const dirPath = ensurePathWithin(rootDir, relativeDir);
      const entries = readdirSync(dirPath, { withFileTypes: true }).map((entry) => {
        const stat = statSync(join(dirPath, entry.name));
        return {
          name: entry.name,
          path: relativeDir ? `${relativeDir}/${entry.name}` : entry.name,
          kind: entry.isDirectory() ? 'directory' : 'file',
          size: stat.size,
          modifiedAt: stat.mtime.toISOString(),
        };
      });
      res.json({ basePath: 'output', path: relativeDir, entries });
    } catch (err) {
      logger.error(`Shared files API error: ${err}`);
      res.status(500).json({ error: 'Failed to list files' });
    }
  });

  app.get('/api/shared/:token/files/content', async (req: Request, res: Response) => {
    try {
      const task = await repo.getLocalTaskByShareToken(req.params.token);
      if (!task || !task.workspacePath) { res.status(404).json({ error: 'Not found' }); return; }

      const relativePath = String(req.query.path ?? '').replace(/^\/+/, '');
      if (!relativePath) { res.status(400).json({ error: 'path is required' }); return; }

      const rootDir = join(task.workspacePath, 'output');
      const filePath = ensurePathWithin(rootDir, relativePath);
      const stat = statSync(filePath);
      if (!stat.isFile()) { res.status(400).json({ error: 'path must point to a file' }); return; }

      res.setHeader('Content-Type', 'text/plain; charset=utf-8');
      res.send(readFileSync(filePath, 'utf-8'));
    } catch (err) {
      logger.error(`Shared file content API error: ${err}`);
      res.status(500).json({ error: 'Failed to read file' });
    }
  });

  app.get('/api/shared/:token/files/raw', async (req: Request, res: Response) => {
    try {
      const task = await repo.getLocalTaskByShareToken(req.params.token);
      if (!task || !task.workspacePath) { res.status(404).json({ error: 'Not found' }); return; }

      const relativePath = String(req.query.path ?? '').replace(/^\/+/, '');
      if (!relativePath) { res.status(400).json({ error: 'path is required' }); return; }

      const rootDir = join(task.workspacePath, 'output');
      const filePath = ensurePathWithin(rootDir, relativePath);
      const stat = statSync(filePath);
      if (!stat.isFile()) { res.status(400).json({ error: 'path must point to a file' }); return; }

      res.type(extname(filePath) || 'application/octet-stream');
      res.send(readFileSync(filePath));
    } catch (err) {
      logger.error(`Shared file raw API error: ${err}`);
      res.status(500).json({ error: 'Failed to read file' });
    }
  });

  app.get('/api/shared/:token/subtasks/activities', async (req: Request, res: Response) => {
    try {
      const task = await repo.getLocalTaskByShareToken(req.params.token);
      if (!task) { res.status(404).json({ error: 'Not found' }); return; }

      const latestJob = await repo.getLatestJobForIssue(localTaskRepoName(task.id), task.id);
      if (!latestJob) { res.json({ subtasks: [] }); return; }

      const subJobs = await repo.getSubJobs(latestJob.id);
      const subtasks = subJobs.map(job => ({
        jobId: job.id,
        issueNumber: job.issueNumber,
        status: job.status,
        currentMovement: job.currentMovement ?? null,
        currentActivity: job.currentActivity ?? null,
        activityLog: '',
      }));
      res.json({ subtasks });
    } catch (err) {
      logger.error(`Shared subtask activities API error: ${err}`);
      res.status(500).json({ error: 'Failed to fetch subtask activities' });
    }
  });

  // ── 認証付きエンドポイント ──

  app.post('/api/local/tasks/:taskId/share', express.json(), async (req: Request, res: Response) => {
    try {
      const taskId = parseTaskId(req.params.taskId);
      if (taskId === null) { res.status(400).json({ error: 'Invalid task ID' }); return; }
      const viewer = req.user as Express.User | undefined;
      const task = await repo.getLocalTask(taskId, viewer ? { viewer } : undefined);
      if (!checkTaskOwnership(req, res, task)) return;

      const shareToken = await repo.shareLocalTask(taskId);
      res.json({ shareToken, shareUrl: `/ui/shared/${shareToken}` });
    } catch (err) {
      logger.error(`Share task API error: ${err}`);
      res.status(500).json({ error: 'Failed to share task' });
    }
  });

  app.delete('/api/local/tasks/:taskId/share', async (req: Request, res: Response) => {
    try {
      const taskId = parseTaskId(req.params.taskId);
      if (taskId === null) { res.status(400).json({ error: 'Invalid task ID' }); return; }
      const viewer = req.user as Express.User | undefined;
      const task = await repo.getLocalTask(taskId, viewer ? { viewer } : undefined);
      if (!checkTaskOwnership(req, res, task)) return;

      await repo.unshareLocalTask(taskId);
      res.json({ ok: true });
    } catch (err) {
      logger.error(`Unshare task API error: ${err}`);
      res.status(500).json({ error: 'Failed to unshare task' });
    }
  });
}
