import { Request, Response, NextFunction, Router } from 'express';
import { StorageService, FileSearchFilters } from '../services/storage.service';

export class StorageController {
  public router: Router;

  constructor(private readonly storageService: StorageService) {
    this.router = Router();
    this.initializeRoutes();
  }

  private initializeRoutes(): void {
    // Buckets
    this.router.get('/buckets', this.findAllBuckets.bind(this));
    this.router.get('/buckets/:id', this.findBucket.bind(this));
    this.router.post('/buckets', this.createBucket.bind(this));
    this.router.patch('/buckets/:id', this.updateBucket.bind(this));
    this.router.delete('/buckets/:id', this.deleteBucket.bind(this));

    // Folders
    this.router.get('/buckets/:bucketId/folders', this.findFoldersInBucket.bind(this));
    this.router.get('/folders/:id', this.findFolder.bind(this));
    this.router.post('/folders', this.createFolder.bind(this));
    this.router.patch('/folders/:id', this.updateFolder.bind(this));
    this.router.delete('/folders/:id', this.deleteFolder.bind(this));

    // Files
    this.router.get('/buckets/:bucketId/files', this.findFilesInFolder.bind(this));
    this.router.get('/files/:id', this.findFile.bind(this));
    this.router.get('/files/key/:storageKey', this.findFileByStorageKey.bind(this));
    this.router.post('/files', this.createFile.bind(this));
    this.router.patch('/files/:id', this.updateFile.bind(this));
    this.router.delete('/files/:id', this.deleteFile.bind(this));
    this.router.post('/files/:id/download', this.incrementDownloadCount.bind(this));

    // Search & Stats
    this.router.get('/search', this.searchFiles.bind(this));
    this.router.get('/usage', this.getStorageUsage.bind(this));
    this.router.get('/recent', this.findRecentFiles.bind(this));
  }

  // ============================================
  // BUCKETS
  // ============================================

  private async findAllBuckets(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const buckets = await this.storageService.findAllBuckets(tenantId);
      res.json({ data: buckets, total: buckets.length });
    } catch (error) {
      next(error);
    }
  }

  private async findBucket(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const bucket = await this.storageService.findBucket(id);

      if (!bucket) {
        res.status(404).json({ error: 'Bucket not found' });
        return;
      }

      res.json({ data: bucket });
    } catch (error) {
      next(error);
    }
  }

  private async createBucket(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;

      const bucket = await this.storageService.createBucket(tenantId, req.body, userId);
      res.status(201).json({ data: bucket });
    } catch (error) {
      next(error);
    }
  }

  private async updateBucket(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const bucket = await this.storageService.updateBucket(id, req.body);

      if (!bucket) {
        res.status(404).json({ error: 'Bucket not found' });
        return;
      }

      res.json({ data: bucket });
    } catch (error) {
      next(error);
    }
  }

  private async deleteBucket(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const deleted = await this.storageService.deleteBucket(id);

      if (!deleted) {
        res.status(404).json({ error: 'Bucket not found' });
        return;
      }

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // FOLDERS
  // ============================================

  private async findFoldersInBucket(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { bucketId } = req.params;
      const parentId = req.query.parentId as string | undefined;

      const folders = await this.storageService.findFoldersInBucket(bucketId, parentId);
      res.json({ data: folders, total: folders.length });
    } catch (error) {
      next(error);
    }
  }

  private async findFolder(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const folder = await this.storageService.findFolder(id);

      if (!folder) {
        res.status(404).json({ error: 'Folder not found' });
        return;
      }

      res.json({ data: folder });
    } catch (error) {
      next(error);
    }
  }

  private async createFolder(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;
      const { bucketId, ...data } = req.body;

      const folder = await this.storageService.createFolder(tenantId, bucketId, data, userId);
      res.status(201).json({ data: folder });
    } catch (error) {
      next(error);
    }
  }

  private async updateFolder(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const folder = await this.storageService.updateFolder(id, req.body);

      if (!folder) {
        res.status(404).json({ error: 'Folder not found' });
        return;
      }

      res.json({ data: folder });
    } catch (error) {
      next(error);
    }
  }

  private async deleteFolder(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const recursive = req.query.recursive === 'true';

      const deleted = await this.storageService.deleteFolder(id, recursive);

      if (!deleted) {
        res.status(404).json({ error: 'Folder not found' });
        return;
      }

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // FILES
  // ============================================

  private async findFilesInFolder(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { bucketId } = req.params;
      const folderId = req.query.folderId as string | undefined;
      const filters: FileSearchFilters = {
        category: req.query.category as string,
        mimeType: req.query.mimeType as string,
        uploadedBy: req.query.uploadedBy as string,
      };

      if (req.query.isPublic !== undefined) {
        filters.isPublic = req.query.isPublic === 'true';
      }

      const files = await this.storageService.findFilesInFolder(bucketId, folderId, filters);
      res.json({ data: files, total: files.length });
    } catch (error) {
      next(error);
    }
  }

  private async findFile(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const file = await this.storageService.findFile(id);

      if (!file) {
        res.status(404).json({ error: 'File not found' });
        return;
      }

      res.json({ data: file });
    } catch (error) {
      next(error);
    }
  }

  private async findFileByStorageKey(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { storageKey } = req.params;
      const file = await this.storageService.findFileByStorageKey(storageKey);

      if (!file) {
        res.status(404).json({ error: 'File not found' });
        return;
      }

      res.json({ data: file });
    } catch (error) {
      next(error);
    }
  }

  private async createFile(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const userId = req.headers['x-user-id'] as string;
      const { bucketId, ...data } = req.body;

      const file = await this.storageService.createFile(tenantId, bucketId, data, userId);
      res.status(201).json({ data: file });
    } catch (error) {
      next(error);
    }
  }

  private async updateFile(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const file = await this.storageService.updateFile(id, req.body);

      if (!file) {
        res.status(404).json({ error: 'File not found' });
        return;
      }

      res.json({ data: file });
    } catch (error) {
      next(error);
    }
  }

  private async deleteFile(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      const deleted = await this.storageService.deleteFile(id);

      if (!deleted) {
        res.status(404).json({ error: 'File not found' });
        return;
      }

      res.status(204).send();
    } catch (error) {
      next(error);
    }
  }

  private async incrementDownloadCount(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const { id } = req.params;
      await this.storageService.incrementDownloadCount(id);
      res.json({ data: { success: true } });
    } catch (error) {
      next(error);
    }
  }

  // ============================================
  // SEARCH & STATS
  // ============================================

  private async searchFiles(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const query = req.query.q as string;

      if (!query) {
        res.status(400).json({ error: 'Query parameter "q" is required' });
        return;
      }

      const filters: FileSearchFilters = {
        category: req.query.category as string,
        mimeType: req.query.mimeType as string,
      };

      if (req.query.isPublic !== undefined) {
        filters.isPublic = req.query.isPublic === 'true';
      }

      const files = await this.storageService.searchFiles(tenantId, query, filters);
      res.json({ data: files, total: files.length });
    } catch (error) {
      next(error);
    }
  }

  private async getStorageUsage(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const usage = await this.storageService.getStorageUsage(tenantId);
      res.json({ data: usage });
    } catch (error) {
      next(error);
    }
  }

  private async findRecentFiles(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
      const tenantId = req.headers['x-tenant-id'] as string;
      const limit = parseInt(req.query.limit as string) || 20;

      const files = await this.storageService.findRecentFiles(tenantId, limit);
      res.json({ data: files, total: files.length });
    } catch (error) {
      next(error);
    }
  }
}
