import { Response, NextFunction } from 'express';
import path from 'path';
import { PutObjectCommand } from '@aws-sdk/client-s3';
import { s3Client, BUCKET_NAME } from '../utils/s3';
import { AssetService } from '../services/asset.service';
import { ScraperService } from '../services/scraper.service';
import { AuthRequest } from '../middleware/auth.middleware';

export class AssetController {
  private assetService = new AssetService();
  private scraperService = new ScraperService();

  public scrapeCaseStudy = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const url = req.query.url as string;
      if (!url) {
        return res.status(400).json({ error: 'URL parameter is required' });
      }
      const data = await this.scraperService.scrapeCaseStudy(url);
      res.status(200).json(data);
    } catch (err) { next(err); }
  }

  public uploadAsset = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const isUrlAsset = req.body.isUrlAsset === 'true' || req.body.isUrlAsset === true || req.body.type === 'url' || req.body.type === 'case_study';
      
      if (!isUrlAsset && !req.file) {
        throw new Error('No file uploaded');
      }
      
      const uploaderId = req.user?.userId || 'system';
      
      const description = req.body.description;
      if (!description || !description.trim()) {
        return res.status(400).json({ error: 'Description is required' });
      }
      
      // Parse shares if present
      let shares = req.body.shares;
      if (typeof shares === 'string' && shares.trim()) {
        try { shares = JSON.parse(shares); } catch { shares = undefined; }
      }

      let fileUrl = '';
      if (!isUrlAsset && req.file) {
        const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
        const filename = uniqueSuffix + path.extname(req.file.originalname);
        
        await s3Client.send(new PutObjectCommand({
          Bucket: BUCKET_NAME,
          Key: filename,
          Body: req.file.buffer,
          ContentType: req.file.mimetype,
        }));
        
        fileUrl = `/uploads/${filename}`;
      } else {
        fileUrl = req.body.url;
      }

      const assetData = {
        title: req.body.title || (req.file ? req.file.originalname : 'URL Asset'),
        type: req.body.type || (isUrlAsset ? 'url' : req.file!.mimetype),
        size: isUrlAsset ? 0 : req.file!.size,
        url: fileUrl,
        uploadedBy: uploaderId,
        description: req.body.description || null,
        categoryId: req.body.categoryId || null,
        subcategory: req.body.subcategory || null,
        tags: req.body.tags || [],
        githubUrl: req.body.githubUrl || null,
        status: req.body.status || 'published',
        isDownloadable: req.body.isDownloadable === 'true' || req.body.isDownloadable === true,
        thumbnailUrl: req.body.thumbnailUrl || null,
        problemStatement: req.body.problemStatement || null,
        solution: req.body.solution || null,
        shares,
        sharedOrgIds: req.body.sharedOrgIds || null,
      };

      const asset = await this.assetService.createAsset(assetData);
      res.status(201).json(asset);
    } catch (err) { next(err); }
  }

  public listAssets = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const userContext = req.user ? { role: req.user.role, userId: req.user.userId } : undefined;
      const assets = await this.assetService.getAssets(userContext);
      res.status(200).json(assets);
    } catch(err) { next(err); }
  }

  public getAsset = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const asset = await this.assetService.getAssetById(req.params.id);
      if (!asset) {
        return res.status(404).json({ error: 'Asset not found' });
      }
      res.status(200).json(asset);
    } catch (err) { next(err); }
  }

  public updateAsset = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const asset = await this.assetService.updateAsset(req.params.id, req.body);
      res.status(200).json(asset);
    } catch (err) { next(err); }
  }

  public bulkShareAssets = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const { assetIds, shares } = req.body;
      if (!Array.isArray(assetIds)) {
        return res.status(400).json({ error: 'assetIds must be an array' });
      }
      if (!Array.isArray(shares)) {
        return res.status(400).json({ error: 'shares must be an array' });
      }
      await this.assetService.bulkShareAssets(assetIds, shares);
      res.status(200).json({ message: 'Assets shared successfully' });
    } catch (err) { next(err); }
  }

  public shareAsset = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const { organizationIds } = req.body;
      if (!Array.isArray(organizationIds)) {
        return res.status(400).json({ error: 'organizationIds must be an array' });
      }
      const asset = await this.assetService.shareAsset(req.params.id, organizationIds);
      res.status(200).json(asset);
    } catch (err) { next(err); }
  }

  public unshareAsset = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const { organizationIds } = req.body;
      if (!Array.isArray(organizationIds)) {
        return res.status(400).json({ error: 'organizationIds must be an array' });
      }
      const asset = await this.assetService.unshareAsset(req.params.id, organizationIds);
      res.status(200).json(asset);
    } catch (err) { next(err); }
  }

  public incrementDownload = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const asset = await this.assetService.incrementDownloadCount(req.params.id);
      res.status(200).json(asset);
    } catch (err) { next(err); }
  }

  public deleteAsset = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      await this.assetService.deleteAsset(req.params.id);
      res.status(204).send();
    } catch(err) { next(err); }
  }

  public requestDownload = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const userId = req.user?.userId;
      if (!userId) return res.status(401).json({ error: 'Unauthorized' });
      const request = await this.assetService.requestDownload(req.params.id, userId);
      res.status(200).json(request);
    } catch (err) { next(err); }
  }

  public approveDownload = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const request = await this.assetService.approveDownloadRequest(req.params.requestId);
      res.status(200).json(request);
    } catch (err) { next(err); }
  }

  public rejectDownload = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const request = await this.assetService.rejectDownloadRequest(req.params.requestId);
      res.status(200).json(request);
    } catch (err) { next(err); }
  }

  public createAssetGroup = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const { name, description, assetIds } = req.body;
      if (!name) {
        return res.status(400).json({ error: 'Group name is required' });
      }
      const group = await this.assetService.createAssetGroup(name, description, assetIds || []);
      res.status(201).json(group);
    } catch (err) { next(err); }
  }

  public listAssetGroups = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const groups = await this.assetService.getAssetGroups();
      res.status(200).json(groups);
    } catch (err) { next(err); }
  }

  public deleteAssetGroup = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      await this.assetService.deleteAssetGroup(req.params.id);
      res.status(204).send();
    } catch (err) { next(err); }
  }

  public addAssetsToGroup = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const { assetIds } = req.body;
      if (!Array.isArray(assetIds)) {
        return res.status(400).json({ error: 'assetIds must be an array' });
      }
      const group = await this.assetService.addAssetsToGroup(req.params.id, assetIds);
      res.status(200).json(group);
    } catch (err) { next(err); }
  }

  public removeAssetsFromGroup = async (req: AuthRequest, res: Response, next: NextFunction) => {
    try {
      const { assetIds } = req.body;
      if (!Array.isArray(assetIds)) {
        return res.status(400).json({ error: 'assetIds must be an array' });
      }
      const group = await this.assetService.removeAssetsFromGroup(req.params.id, assetIds);
      res.status(200).json(group);
    } catch (err) { next(err); }
  }
}
