import {
  Controller,
  Get,
  Post,
  Delete,
  Body,
  Param,
  Query,
  Res,
  UploadedFile,
  UseInterceptors,
  ParseIntPipe,
  HttpCode,
  HttpStatus,
  StreamableFile,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { Response } from 'express';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiBearerAuth,
  ApiParam,
  ApiQuery,
  ApiConsumes,
  ApiBody,
} from '@nestjs/swagger';
import { NextcloudService } from './nextcloud.service';
import {
  ListFilesDto,
  CreateShareDto,
  ListEventsDto,
  NextcloudFile,
  FileResponseDto,
  ShareResponseDto,
  UserResponseDto,
  CalendarEventResponseDto,
  ShareType,
  SharePermission,
} from './nextcloud.types';
import { RequirePermissions } from '../../../../auth/permissions/permissions.decorator';
import { Permission } from '../../../../auth/permissions/permissions.enum';

/**
 * Nextcloud Integration Controller
 *
 * REST API endpoints for Nextcloud cloud storage integration.
 */
@ApiTags('Integrations - Nextcloud')
@ApiBearerAuth()
@Controller('integrations/nextcloud')
export class NextcloudController {
  constructor(private readonly nextcloudService: NextcloudService) {}

  // ============ Health & Status ============

  @Post('test')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Test Nextcloud connection' })
  @ApiResponse({
    status: 200,
    description: 'Connection test result',
    schema: {
      type: 'object',
      properties: {
        success: { type: 'boolean' },
        message: { type: 'string' },
        latency: { type: 'number' },
      },
    },
  })
  @RequirePermissions(Permission.INTEGRATIONS_MANAGE)
  async testConnection() {
    return this.nextcloudService.testConnection();
  }

  @Get('health')
  @ApiOperation({ summary: 'Get Nextcloud connector health status' })
  @ApiResponse({
    status: 200,
    description: 'Health status',
    schema: {
      type: 'object',
      properties: {
        status: { type: 'string', enum: ['connected', 'error', 'not_configured'] },
        lastCheck: { type: 'string', format: 'date-time' },
        latency: { type: 'number' },
        error: { type: 'string' },
      },
    },
  })
  @RequirePermissions(Permission.INTEGRATIONS_VIEW)
  async getHealth() {
    return this.nextcloudService.checkHealth();
  }

  // ============ Files ============

  @Get('files')
  @ApiOperation({ summary: 'List files in a directory' })
  @ApiQuery({ name: 'path', required: false, description: 'Directory path (default: root)' })
  @ApiQuery({ name: 'filesOnly', required: false, type: Boolean })
  @ApiQuery({ name: 'dirsOnly', required: false, type: Boolean })
  @ApiQuery({ name: 'depth', required: false, type: Number })
  @ApiResponse({
    status: 200,
    description: 'List of files',
    type: [FileResponseDto],
  })
  @RequirePermissions(Permission.INTEGRATIONS_VIEW)
  async listFiles(@Query() query: ListFilesDto) {
    return this.nextcloudService.listFiles(query);
  }

  @Get('files/recent')
  @ApiOperation({ summary: 'List recently modified files' })
  @ApiQuery({ name: 'limit', required: false, type: Number, description: 'Maximum number of files to return (default: 20)' })
  @ApiResponse({ status: 200, description: 'List of recently modified files', type: [FileResponseDto] })
  @RequirePermissions(Permission.INTEGRATIONS_VIEW)
  async getRecentFiles(@Query('limit') limit?: string) {
    const fileLimit = limit ? parseInt(limit, 10) : 20;
    const result = await this.nextcloudService.listFiles({ path: '/' });
    // Sort by modification date (newest first) and limit
    const files = Array.isArray(result) ? result : [];
    return files
      .filter((f) => f.type !== 'directory')
      .sort((a, b) => {
        const dateA = new Date(a.lastmod || 0).getTime();
        const dateB = new Date(b.lastmod || 0).getTime();
        return dateB - dateA;
      })
      .slice(0, fileLimit);
  }

  @Get('files/*')
  @ApiOperation({ summary: 'Get file info or download file' })
  @ApiParam({ name: 'path', description: 'File path (use * wildcard in route)' })
  @ApiQuery({ name: 'download', required: false, type: Boolean, description: 'Download file content' })
  @ApiResponse({ status: 200, description: 'File info or content' })
  @ApiResponse({ status: 404, description: 'File not found' })
  @RequirePermissions(Permission.INTEGRATIONS_VIEW)
  async getFile(
    @Param('0') path: string,
    @Query('download') download?: string,
    @Res({ passthrough: true }) res?: Response,
  ): Promise<NextcloudFile | StreamableFile> {
    const filePath = `/${path}`;

    if (download === 'true') {
      const content = await this.nextcloudService.downloadFile(filePath);
      const fileInfo = await this.nextcloudService.getFileInfo(filePath);

      res?.set({
        'Content-Type': fileInfo.mime || 'application/octet-stream',
        'Content-Disposition': `attachment; filename="${fileInfo.basename}"`,
        'Content-Length': content.length,
      });

      return new StreamableFile(content);
    }

    return this.nextcloudService.getFileInfo(filePath);
  }

  @Post('files/upload')
  @UseInterceptors(FileInterceptor('file'))
  @ApiOperation({ summary: 'Upload a file' })
  @ApiConsumes('multipart/form-data')
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        file: { type: 'string', format: 'binary' },
        path: { type: 'string', description: 'Destination path including filename' },
      },
      required: ['file', 'path'],
    },
  })
  @ApiResponse({ status: 201, description: 'File uploaded', type: FileResponseDto })
  @RequirePermissions(Permission.INTEGRATIONS_MANAGE)
  async uploadFile(
    @UploadedFile() file: Express.Multer.File,
    @Body('path') path: string,
  ) {
    return this.nextcloudService.uploadFile(path, file.buffer, file.mimetype);
  }

  @Post('files/folder')
  @HttpCode(HttpStatus.CREATED)
  @ApiOperation({ summary: 'Create a folder' })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        path: { type: 'string', description: 'Folder path to create' },
      },
      required: ['path'],
    },
  })
  @ApiResponse({ status: 201, description: 'Folder created', type: FileResponseDto })
  @RequirePermissions(Permission.INTEGRATIONS_MANAGE)
  async createFolder(@Body('path') path: string) {
    return this.nextcloudService.createFolder(path);
  }

  @Delete('files/*')
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: 'Delete a file or folder' })
  @ApiParam({ name: 'path', description: 'File/folder path' })
  @ApiResponse({ status: 204, description: 'File/folder deleted' })
  @ApiResponse({ status: 404, description: 'Not found' })
  @RequirePermissions(Permission.INTEGRATIONS_MANAGE)
  async deleteFile(@Param('0') path: string): Promise<void> {
    await this.nextcloudService.deleteFile(`/${path}`);
  }

  @Post('files/move')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Move a file or folder' })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        source: { type: 'string' },
        destination: { type: 'string' },
        overwrite: { type: 'boolean', default: false },
      },
      required: ['source', 'destination'],
    },
  })
  @ApiResponse({ status: 200, description: 'File moved' })
  @RequirePermissions(Permission.INTEGRATIONS_MANAGE)
  async moveFile(
    @Body('source') source: string,
    @Body('destination') destination: string,
    @Body('overwrite') overwrite?: boolean,
  ) {
    await this.nextcloudService.moveFile(source, destination, overwrite);
    return { success: true, message: `Moved ${source} to ${destination}` };
  }

  @Post('files/copy')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Copy a file or folder' })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        source: { type: 'string' },
        destination: { type: 'string' },
        overwrite: { type: 'boolean', default: false },
      },
      required: ['source', 'destination'],
    },
  })
  @ApiResponse({ status: 200, description: 'File copied' })
  @RequirePermissions(Permission.INTEGRATIONS_MANAGE)
  async copyFile(
    @Body('source') source: string,
    @Body('destination') destination: string,
    @Body('overwrite') overwrite?: boolean,
  ) {
    await this.nextcloudService.copyFile(source, destination, overwrite);
    return { success: true, message: `Copied ${source} to ${destination}` };
  }

  // ============ Shares ============

  @Get('shares')
  @ApiOperation({ summary: 'Get shares' })
  @ApiQuery({ name: 'path', required: false, description: 'Filter by path' })
  @ApiResponse({ status: 200, description: 'List of shares', type: [ShareResponseDto] })
  @RequirePermissions(Permission.INTEGRATIONS_VIEW)
  async getShares(@Query('path') path?: string) {
    return this.nextcloudService.getShares(path);
  }

  @Post('shares')
  @HttpCode(HttpStatus.CREATED)
  @ApiOperation({ summary: 'Create a share' })
  @ApiResponse({ status: 201, description: 'Share created', type: ShareResponseDto })
  @RequirePermissions(Permission.INTEGRATIONS_MANAGE)
  async createShare(@Body() data: CreateShareDto) {
    return this.nextcloudService.createShare(data);
  }

  @Post('shares/public-link')
  @HttpCode(HttpStatus.CREATED)
  @ApiOperation({ summary: 'Create a public link share' })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        path: { type: 'string' },
        password: { type: 'string' },
        expireDate: { type: 'string', format: 'date' },
        permissions: { type: 'number', description: 'Permission bitmask (1=read, 2=update, 4=create, 8=delete)' },
      },
      required: ['path'],
    },
  })
  @ApiResponse({ status: 201, description: 'Public link created', type: ShareResponseDto })
  @RequirePermissions(Permission.INTEGRATIONS_MANAGE)
  async createPublicLink(
    @Body('path') path: string,
    @Body('password') password?: string,
    @Body('expireDate') expireDate?: string,
    @Body('permissions') permissions?: number,
  ) {
    return this.nextcloudService.createPublicLink(path, { password, expireDate, permissions });
  }

  @Delete('shares/:id')
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: 'Delete a share' })
  @ApiParam({ name: 'id', type: Number })
  @ApiResponse({ status: 204, description: 'Share deleted' })
  @RequirePermissions(Permission.INTEGRATIONS_MANAGE)
  async deleteShare(@Param('id', ParseIntPipe) id: number): Promise<void> {
    await this.nextcloudService.deleteShare(id);
  }

  // ============ User ============

  @Get('user')
  @ApiOperation({ summary: 'Get current user info' })
  @ApiResponse({ status: 200, description: 'User info', type: UserResponseDto })
  @RequirePermissions(Permission.INTEGRATIONS_VIEW)
  async getCurrentUser() {
    return this.nextcloudService.getCurrentUser();
  }

  @Get('user/quota')
  @ApiOperation({ summary: 'Get user storage quota' })
  @ApiResponse({
    status: 200,
    description: 'Quota info',
    schema: {
      type: 'object',
      properties: {
        used: { type: 'number' },
        total: { type: 'number' },
        free: { type: 'number' },
        relative: { type: 'number' },
      },
    },
  })
  @RequirePermissions(Permission.INTEGRATIONS_VIEW)
  async getUserQuota() {
    return this.nextcloudService.getUserQuota();
  }

  // ============ Calendar ============

  @Get('calendars')
  @ApiOperation({ summary: 'List calendars' })
  @ApiResponse({
    status: 200,
    description: 'List of calendars',
    schema: {
      type: 'array',
      items: {
        type: 'object',
        properties: {
          id: { type: 'string' },
          displayName: { type: 'string' },
          color: { type: 'string' },
          isWritable: { type: 'boolean' },
        },
      },
    },
  })
  @RequirePermissions(Permission.INTEGRATIONS_VIEW)
  async listCalendars() {
    return this.nextcloudService.listCalendars();
  }

  @Get('calendar/events')
  @ApiOperation({ summary: 'List calendar events' })
  @ApiQuery({ name: 'calendarId', required: false, description: 'Calendar ID (default: personal)' })
  @ApiQuery({ name: 'start', required: false, description: 'Start date filter (ISO 8601)' })
  @ApiQuery({ name: 'end', required: false, description: 'End date filter (ISO 8601)' })
  @ApiResponse({ status: 200, description: 'List of events', type: [CalendarEventResponseDto] })
  @RequirePermissions(Permission.INTEGRATIONS_VIEW)
  async listEvents(@Query() query: ListEventsDto) {
    return this.nextcloudService.listEvents(query);
  }

  @Get('calendar/events/today')
  @ApiOperation({ summary: 'Get today\'s events' })
  @ApiQuery({ name: 'calendarId', required: false })
  @ApiResponse({ status: 200, description: 'Today\'s events', type: [CalendarEventResponseDto] })
  @RequirePermissions(Permission.INTEGRATIONS_VIEW)
  async getTodayEvents(@Query('calendarId') calendarId?: string) {
    return this.nextcloudService.getTodayEvents(calendarId);
  }
}
