import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
  Query,
} from '@nestjs/common';
import * as path from 'path';
import { AppService } from './app.service';
import { EventsGateway } from './events.gateway';

interface FileRecord {
  dataset: string;
  input: string;
  output: string;
  date: string;
}

@Controller()
export class AppController {
  constructor(
    private readonly appService: AppService,
    private readonly eventsGateway: EventsGateway,
  ) {}

  // List available datasets
  @Get('files')
  listDatasets() {
    return this.appService.listDatasets();
  }

  // List all datasets (including disabled ones)
  @Get('files/all-datasets')
  listAllDatasets() {
    return this.appService.listAllDatasets();
  }

  // Get total successful files across all datasets
  @Get('files/stats/successful')
  getTotalSuccessfulFiles() {
    return this.appService.getTotalSuccessfulFiles();
  }

  // Get total processed files across all datasets
  @Get('files/stats/processed')
  getTotalProcessedFiles() {
    return this.appService.getTotalProcessedFiles();
  }

  @Get()
  getRoot() {
    return {
      status: 'ok',
      message: 'Watch Finished API Service',
      datetime: new Date().toISOString(),
      uptime: process.uptime(),
    };
  }

  @Get('ready')
  getReady() {
    return { status: 'ready', datetime: new Date().toISOString() };
  }

  @Get('health')
  getHealth() {
    return { status: 'healthy', datetime: new Date().toISOString() };
  }

  // --- Unified files CRUD endpoints below ---

  // Create a file record
  @Post('files/:dataset/:file')
  createFile(
    @Param('dataset') dataset: string,
    @Param('file') file: string,
    @Body() payload: any,
  ) {
    const result = this.appService.setFile(dataset, file, payload);
    this.eventsGateway.emitFileUpdate({
      type: 'created',
      dataset,
      file,
      data: payload,
    });
    return result;
  }

  // Update a file record
  @Post('files/:dataset/:file/update')
  updateFile(
    @Param('dataset') dataset: string,
    @Param('file') file: string,
    @Body() payload: any,
  ) {
    const result = this.appService.setFile(dataset, file, payload);
    this.eventsGateway.emitFileUpdate({
      type: 'updated',
      dataset,
      file,
      data: payload,
    });
    return result;
  }

  // Read a file record
  @Get('files/:dataset/:file')
  readFile(@Param('dataset') dataset: string, @Param('file') file: string) {
    return this.appService.findFile(dataset, file);
  }

  // Destroy a file record (hard delete)
  @Delete('files/:dataset/:file')
  destroyFile(@Param('dataset') dataset: string, @Param('file') file: string) {
    const result = this.appService.removeFile(dataset, file, false);
    this.eventsGateway.emitFileUpdate({
      type: 'deleted',
      dataset,
      file,
    });
    return result;
  }

  // Clear all file records from database
  @Delete('files')
  clearAllFiles() {
    const result = this.appService.clearAllFiles();
    // Emit a general file update to notify clients
    this.eventsGateway.emitFileUpdate({
      type: 'cleared',
      dataset: '*',
      file: '*',
    });
    return { message: 'All file records cleared' };
  }

  // Requeue a file for processing (creates a task)
  @Post('files/:dataset/:file/requeue')
  requeueFile(
    @Param('dataset') dataset: string,
    @Param('file') file: string,
    @Body('preset') preset?: string,
  ) {
    // Get dataset configuration to find the preset and output configuration
    const datasetConfig = this.appService.getDatasetConfig();
    let processingPreset = preset;
    let output: string;

    // Try to find existing file record first
    const fileRecord = this.appService.findFile(dataset, file) as FileRecord;

    // Determine preset from dataset config (same logic as watcher)
    if (!processingPreset) {
      processingPreset = 'Fast 1080p30'; // Default fallback
    }

    if (datasetConfig[dataset]) {
      const datasetObj = datasetConfig[dataset];

      // Find the path configuration that matches this file
      for (const pathKey of Object.keys(datasetObj)) {
        if (pathKey !== 'enabled' && file.startsWith(pathKey)) {
          const pathConfig = datasetObj[pathKey];
          if (pathConfig && pathConfig.preset) {
            processingPreset = pathConfig.preset;
            break;
          }
        }
      }

      // Fallback to dataset-level preset if no path-specific preset found
      if (processingPreset === 'Fast 1080p30' && datasetObj.preset) {
        processingPreset = datasetObj.preset;
      }
    }

    console.log(`Final processingPreset: ${processingPreset}`);

    // Determine destination and ext from dataset config
    let destination: string | undefined;
    let ext = '.mkv'; // Default extension

    if (datasetConfig[dataset]) {
      const datasetObj = datasetConfig[dataset];

      // Find the path configuration that matches this file
      for (const pathKey of Object.keys(datasetObj)) {
        if (pathKey !== 'enabled' && file.startsWith(pathKey)) {
          const pathConfig = datasetObj[pathKey];
          if (pathConfig) {
            if (pathConfig.destination) {
              destination = pathConfig.destination;
            }
            if (pathConfig.ext) {
              ext = pathConfig.ext.startsWith('.')
                ? pathConfig.ext
                : '.' + pathConfig.ext;
            }
          }
          break;
        }
      }

      // Fallback to dataset-level config
      if (!destination && datasetObj.destination) {
        destination = datasetObj.destination;
      }
      if (ext === '.mkv' && datasetObj.ext) {
        ext = datasetObj.ext.startsWith('.')
          ? datasetObj.ext
          : '.' + datasetObj.ext;
      }
    }

    if (fileRecord) {
      // Use existing file record
      output = fileRecord.output;
    } else {
      // No existing file record - determine output path from dataset config
      if (destination) {
        const fileName = path.basename(file, path.extname(file));
        output = path.join(destination, fileName + ext);
      } else {
        // Default: same directory with new extension
        output = path.join(
          path.dirname(file),
          path.basename(file, path.extname(file)) + ext,
        );
      }
    }

    // Fallback to default preset if no preset found
    if (!processingPreset) {
      processingPreset = 'Fast 1080p30';
    }

    // Create a task for processing
    const task = this.appService.createTask({
      dataset,
      input: file,
      output: output,
      preset: processingPreset,
      priority: 1, // Higher priority for manual requeues
      status: 'pending', // Mark as pending for processing
    });

    // Check if file already exists
    const existingFile = this.appService.findFile(dataset, file);

    // Update file record (create if doesn't exist, or update output if requeueing)
    this.appService.setFile(dataset, file, {
      date: new Date().toISOString(),
      output: output,
    });

    // Emit file update
    this.eventsGateway.emitFileUpdate({
      type: 'requeued',
      dataset,
      file,
      taskId: task.id,
    });

    // Emit task update to indicate task was created
    this.eventsGateway.emitTaskUpdate({
      type: 'created',
      taskId: task.id,
      task: 'handbrake',
      input: file,
      output: output,
      preset: processingPreset,
    });

    return { taskId: task.id, message: 'Task created for processing' };
  }

  @Get('files/:dataset')
  getAllFiles(@Param('dataset') dataset: string) {
    return this.appService.getAllFiles(dataset);
  }

  @Get('files/:dataset/deleted-older-than/:isoDate')
  getDeletedOlderThan(
    @Param('dataset') dataset: string,
    @Param('isoDate') isoDate: string,
  ) {
    return this.appService.getDeletedOlderThan(dataset, isoDate);
  }

  // --- Additional endpoints below ---

  @Post('handbrake/process')
  processWithHandbrake(
    @Body('input') input: string,
    @Body('output') output: string,
    @Body('preset') preset: string,
  ) {
    this.eventsGateway.emitTaskUpdate({
      type: 'started',
      task: 'handbrake',
      input,
      output,
      preset,
    });
    return this.appService.processWithHandbrake(input, output, preset);
  }

  @Get('handbrake/presets')
  getHandbrakePresets() {
    return this.appService.getHandbrakePresets();
  }

  @Post('maintenance/cleanup')
  cleanup(@Body('file') file: string, @Body('dirs') dirs: string[]) {
    const result = this.appService.cleanup(file, dirs);
    this.eventsGateway.emitMaintenanceUpdate({
      type: 'cleanup',
      file,
      dirs,
    });
    return result;
  }

  @Post('maintenance/purge')
  purge(
    @Body('dirs') dirs: string[],
    @Body('dayMs') dayMs?: number,
    @Body('cleanerMs') cleanerMs?: number,
  ) {
    const result = this.appService.purge(dirs, dayMs, cleanerMs);
    this.eventsGateway.emitMaintenanceUpdate({
      type: 'purge',
      dirs,
      dayMs,
      cleanerMs,
    });
    return result;
  }

  @Post('maintenance/prune')
  prune(@Body('dirs') dirs: string[]) {
    const result = this.appService.prune(dirs);
    this.eventsGateway.emitTaskUpdate({
      type: 'prune',
      dirs,
    });
    return result;
  }

  // Task maintenance endpoints
  @Post('maintenance/tasks/cleanup')
  cleanupTasks(
    @Body('status') status?: string,
    @Body('olderThanDays') olderThanDays?: number,
  ) {
    let result;
    if (status) {
      result = this.appService.cleanupTasksByStatus(status, olderThanDays);
    } else if (olderThanDays) {
      result = this.appService.cleanupOldTasks(olderThanDays);
    } else {
      // Default: cleanup completed and failed tasks older than 7 days
      result = this.appService.cleanupTasksByStatus('completed', 7);
      this.appService.cleanupTasksByStatus('failed', 7);
      this.appService.cleanupTasksByStatus('skipped', 7);
    }
    this.eventsGateway.emitTaskUpdate({
      type: 'task_cleanup',
      status,
      olderThanDays,
    });
    return result;
  }

  @Post('maintenance/tasks/archive')
  archiveTasks(@Body('daysOld') daysOld: number = 30): { changes?: number } {
    const result = this.appService.archiveOldTasks(daysOld);
    this.eventsGateway.emitTaskUpdate({
      type: 'task_archive',
      daysOld,
    });
    return result;
  }

  @Post('maintenance/tasks/purge')
  purgeAllTasks() {
    const result = this.appService.purgeAllTasks();
    this.eventsGateway.emitTaskUpdate({
      type: 'task_purge',
    });
    return result;
  }

  @Get('maintenance/tasks/stats')
  getTaskStats() {
    return this.appService.getTaskStats();
  }

  @Post('maintenance/tasks/scheduled-cleanup')
  runScheduledTaskCleanup() {
    const result = this.appService.scheduledTaskCleanup();
    this.eventsGateway.emitTaskUpdate({
      type: 'scheduled_task_cleanup',
    });
    return result;
  }

  @Get('config/settings')
  getSettings(
    @Query('key') key?: string,
    @Query('default') defaultValue?: any,
  ) {
    return this.appService.getSettings(key, defaultValue);
  }

  @Get('config/settings/:key')
  getSetting(@Param('key') key: string) {
    return this.appService.getSettings(key);
  }

  @Post('config/settings')
  setSettings(@Body() settings: Record<string, any>) {
    // console.log('Received setSettings request:', settings);
    try {
      const result = this.appService.setSettings(settings);
      // console.log('setSettings result:', result);
      this.eventsGateway.emitSettingsUpdate({
        type: 'settings',
        action: 'update',
        settings,
      });
      return result;
    } catch (error) {
      console.error('Error in setSettings controller:', error);
      throw error;
    }
  }

  @Delete('config/settings/:key')
  deleteSetting(@Param('key') key: string) {
    const result = this.appService.deleteSetting(key);
    this.eventsGateway.emitSettingsUpdate({
      type: 'settings',
      action: 'delete',
      key,
    });
    return result;
  }

  @Get('config/file/:name')
  getConfigFile(@Param('name') name: string) {
    return this.appService.getConfigFile(name);
  }

  @Get('config/list')
  listConfigs() {
    return this.appService.listConfigs();
  }

  @Post('watcher/start')
  startWatcher(
    @Body('watches') watches: string[],
    @Body('options') options?: any,
  ) {
    return this.appService.startWatcher(watches, options);
  }

  @Post('watcher/stop')
  async stopWatcher() {
    return this.appService.stopWatcher();
  }

  @Get('watcher/status')
  watcherStatus() {
    return this.appService.watcherStatus();
  }

  @Post('files/expire')
  deleteExpiredFiles(@Body('days') days?: number) {
    return { deleted: this.appService.deleteExpiredFiles(days) };
  }

  @Post('files/migrate')
  migrateJsonToSqlite(@Body() opts: { datasets?: string[]; dataDir?: string }) {
    this.appService.migrateJsonToSqlite(opts);

    return { migrated: true };
  }

  // Task management endpoints
  @Get('tasks')
  getAllTasks() {
    return this.appService.getAllTasks();
  }

  @Get('tasks/queue/status')
  getQueueStatus() {
    return this.appService.getQueueStatus();
  }

  @Get('tasks/queue/settings')
  getQueueSettings() {
    return this.appService.getQueueSettings();
  }

  @Post('tasks/queue/settings')
  updateQueueSettings(@Body() settings: any) {
    return this.appService.updateQueueSettings(settings);
  }

  @Get('tasks/processing-status')
  getTaskProcessingStatus() {
    return this.appService.taskProcessingStatus();
  }

  @Get('tasks/:id')
  getTaskById(@Param('id') id: string) {
    return this.appService.getTaskById(parseInt(id));
  }

  @Delete('tasks/:id')
  deleteTask(@Param('id') id: string) {
    return this.appService.deleteTask(parseInt(id));
  }

  @Post('tasks/start-processing')
  startTaskProcessing() {
    return this.appService.startTaskProcessing();
  }

  @Post('tasks/stop-processing')
  stopTaskProcessing() {
    return this.appService.stopTaskProcessing();
  }

  // Task management endpoints
  @Get('tasks')
  getTasks() {
    return this.appService.getAllTasks();
  }

  @Get('tasks/:id')
  getTask(@Param('id') id: string) {
    return this.appService.getTaskById(parseInt(id));
  }

  @Put('tasks/:id')
  updateTask(@Param('id') id: string, @Body() updates: any) {
    return this.appService.updateTask(parseInt(id), updates);
  }

  @Post('tasks')
  createTask(@Body() taskData: any) {
    return this.appService.createTask(taskData);
  }
}
