// src/utils/nextcloud.ts
import { promises as fs } from 'fs';
import path from 'path';
import crypto from 'crypto';

interface NextcloudConfig {
  endpoint: string;
  username: string;
  password: string;
  uploadPath: string;
  publicBaseUrl: string;
}

interface UploadResult {
  success: boolean;
  url?: string;
  filename?: string;
  error?: string;
  size?: number;
}

interface FileValidation {
  valid: boolean;
  error?: string;
  sanitizedName?: string;
}

export class NextcloudUploader {
  private config: NextcloudConfig;
  private allowedTypes: Set<string>;
  private maxFileSize: number;
  
  constructor() {
    this.config = {
      endpoint: process.env.NEXTCLOUD_ENDPOINT || '',
      username: process.env.NEXTCLOUD_USERNAME || '',
      password: process.env.NEXTCLOUD_PASSWORD || '',
      uploadPath: process.env.NEXTCLOUD_UPLOAD_PATH || '/kb-media',
      publicBaseUrl: process.env.NEXTCLOUD_PUBLIC_URL || ''
    };
    
    this.allowedTypes = new Set([
      'image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml',
      'video/mp4', 'video/webm', 'video/ogg', 'video/avi', 'video/mov',
      'application/pdf',
      'application/msword',
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
      'application/vnd.ms-excel',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      'application/vnd.ms-powerpoint',
      'application/vnd.openxmlformats-officedocument.presentationml.presentation',
      'text/plain', 'text/csv', 'application/json',
      'application/zip', 'application/x-tar', 'application/gzip'
    ]);
    
    this.maxFileSize = 50 * 1024 * 1024; // 50MB
  }
  
  isConfigured(): boolean {
    return !!(this.config.endpoint && 
              this.config.username && 
              this.config.password && 
              this.config.publicBaseUrl);
  }
  
  private validateFile(file: File): FileValidation {
    if (file.size > this.maxFileSize) {
      return {
        valid: false,
        error: `File too large (max ${Math.round(this.maxFileSize / 1024 / 1024)}MB)`
      };
    }
    
    if (!this.allowedTypes.has(file.type)) {
      return {
        valid: false,
        error: `File type not allowed: ${file.type}`
      };
    }
    
    const sanitizedName = this.sanitizeFilename(file.name);
    if (!sanitizedName) {
      return {
        valid: false,
        error: 'Invalid filename'
      };
    }
    
    return {
      valid: true,
      sanitizedName
    };
  }
  
  private sanitizeFilename(filename: string): string {
    const sanitized = filename
      .replace(/[^a-zA-Z0-9._-]/g, '_') // Replace unsafe chars with underscore
      .replace(/_{2,}/g, '_') // Replace multiple underscores with single
      .replace(/^_|_$/g, '') // Remove leading/trailing underscores
      .toLowerCase();
    
    if (sanitized.length > 100) {
      const ext = path.extname(sanitized);
      const base = path.basename(sanitized, ext).substring(0, 90);
      return base + ext;
    }
    
    return sanitized;
  }
  
  private generateUniqueFilename(originalName: string): string {
    const timestamp = Date.now();
    const randomId = crypto.randomBytes(4).toString('hex');
    const ext = path.extname(originalName);
    const base = path.basename(originalName, ext);
    
    return `${timestamp}_${randomId}_${base}${ext}`;
  }
  
  async uploadFile(file: File, category: string = 'general'): Promise<UploadResult> {
    try {
      if (!this.isConfigured()) {
        return {
          success: false,
          error: 'Nextcloud not configured'
        };
      }
      
      const validation = this.validateFile(file);
      if (!validation.valid) {
        return {
          success: false,
          error: validation.error
        };
      }
      
      const uniqueFilename = this.generateUniqueFilename(validation.sanitizedName!);
      
      const categoryPath = this.sanitizeFilename(category);
      const remotePath = `${this.config.uploadPath}/${categoryPath}/${uniqueFilename}`;
      
      const dirPath = `${this.config.uploadPath}/${categoryPath}`;
      await this.ensureDirectoryExists(dirPath);
      
      const arrayBuffer = await file.arrayBuffer();
      const buffer = Buffer.from(arrayBuffer);
      
      const uploadUrl = `${this.config.endpoint}/remote.php/dav/files/${this.config.username}${remotePath}`;
      
      const response = await fetch(uploadUrl, {
        method: 'PUT',
        headers: {
          'Authorization': `Basic ${Buffer.from(`${this.config.username}:${this.config.password}`).toString('base64')}`,
          'Content-Type': file.type,
          'Content-Length': buffer.length.toString()
        },
        body: buffer
      });
      
      if (!response.ok) {
        throw new Error(`Upload failed: ${response.status} ${response.statusText}`);
      }
      
      const publicUrl = await this.createPublicLink(remotePath);
      
      return {
        success: true,
        url: publicUrl,
        filename: uniqueFilename,
        size: file.size
      };
      
    } catch (error) {
      console.error('Nextcloud upload error:', error);
      return {
        success: false,
        error: error instanceof Error ? error.message : 'Upload failed'
      };
    }
  }

  private async ensureDirectoryExists(dirPath: string): Promise<void> {
    try {
      const parts = dirPath.split('/').filter(part => part);
      let currentPath = '';
      
      for (const part of parts) {
        currentPath += '/' + part;
        
        const mkcolUrl = `${this.config.endpoint}/remote.php/dav/files/${this.config.username}${currentPath}`;
        
        const response = await fetch(mkcolUrl, {
          method: 'MKCOL',
          headers: {
            'Authorization': `Basic ${Buffer.from(`${this.config.username}:${this.config.password}`).toString('base64')}`
          }
        });
        
        // 201 = created, 405 = already exists
        if (response.status !== 201 && response.status !== 405) {
          console.warn(`Directory creation failed: ${response.status} for ${currentPath}`);
        }
      }
      
    } catch (error) {
      console.warn('Failed to ensure directory exists:', error);
    }
  }
  
  private async createPublicLink(remotePath: string): Promise<string> {
    try {
      const shareUrl = `${this.config.endpoint}/ocs/v2.php/apps/files_sharing/api/v1/shares`;
      
      const formData = new FormData();
      formData.append('path', remotePath);
      formData.append('shareType', '3'); 
      formData.append('permissions', '1'); 
      
      const response = await fetch(shareUrl, {
        method: 'POST',
        headers: {
          'Authorization': `Basic ${Buffer.from(`${this.config.username}:${this.config.password}`).toString('base64')}`,
          'OCS-APIRequest': 'true'
        },
        body: formData
      });
      
      if (!response.ok) {
        throw new Error('Failed to create public link');
      }
      
      const text = await response.text();
      const urlMatch = text.match(/<url>(.*?)<\/url>/);
      if (urlMatch) {
        return urlMatch[1];
      }
      
      return `${this.config.publicBaseUrl}${remotePath}`;
      
    } catch (error) {
      console.warn('Failed to create public link, using direct URL:', error);
      return `${this.config.publicBaseUrl}${remotePath}`;
    }
  }
  
  async deleteFile(remotePath: string): Promise<{ success: boolean; error?: string }> {
    try {
      if (!this.isConfigured()) {
        return { success: false, error: 'Nextcloud not configured' };
      }
      
      const deleteUrl = `${this.config.endpoint}/remote.php/dav/files/${this.config.username}${remotePath}`;
      
      const response = await fetch(deleteUrl, {
        method: 'DELETE',
        headers: {
          'Authorization': `Basic ${Buffer.from(`${this.config.username}:${this.config.password}`).toString('base64')}`
        }
      });
      
      if (response.ok || response.status === 404) {
        return { success: true };
      }
      
      throw new Error(`Delete failed: ${response.status} ${response.statusText}`);
      
    } catch (error) {
      console.error('Nextcloud delete error:', error);
      return {
        success: false,
        error: error instanceof Error ? error.message : 'Delete failed'
      };
    }
  }
  
  async testConnection(): Promise<{ success: boolean; error?: string; details?: any }> {
    try {
      if (!this.isConfigured()) {
        return { 
          success: false, 
          error: 'Nextcloud not configured',
          details: {
            hasEndpoint: !!this.config.endpoint,
            hasUsername: !!this.config.username,
            hasPassword: !!this.config.password,
            hasPublicUrl: !!this.config.publicBaseUrl
          }
        };
      }
      
      const testUrl = `${this.config.endpoint}/remote.php/dav/files/${this.config.username}/`;
      
      const response = await fetch(testUrl, {
        method: 'PROPFIND',
        headers: {
          'Authorization': `Basic ${Buffer.from(`${this.config.username}:${this.config.password}`).toString('base64')}`,
          'Depth': '0'
        }
      });
      
      if (response.ok) {
        return { 
          success: true,
          details: {
            endpoint: this.config.endpoint,
            username: this.config.username,
            uploadPath: this.config.uploadPath
          }
        };
      }
      
      throw new Error(`Connection failed: ${response.status} ${response.statusText}`);
      
    } catch (error) {
      return {
        success: false,
        error: error instanceof Error ? error.message : 'Connection test failed'
      };
    }
  }
  
  async getFileInfo(remotePath: string): Promise<{ success: boolean; info?: any; error?: string }> {
    try {
      const propfindUrl = `${this.config.endpoint}/remote.php/dav/files/${this.config.username}${remotePath}`;
      
      const response = await fetch(propfindUrl, {
        method: 'PROPFIND',
        headers: {
          'Authorization': `Basic ${Buffer.from(`${this.config.username}:${this.config.password}`).toString('base64')}`,
          'Depth': '0'
        }
      });
      
      if (response.ok) {
        const text = await response.text();
        return {
          success: true,
          info: {
            path: remotePath,
            exists: true,
            response: text.substring(0, 200) + '...'
          }
        };
      }
      
      if (response.status === 404) {
        return {
          success: true,
          info: {
            path: remotePath,
            exists: false
          }
        };
      }
      
      throw new Error(`Failed to get file info: ${response.status}`);
      
    } catch (error) {
      return {
        success: false,
        error: error instanceof Error ? error.message : 'Failed to get file info'
      };
    }
  }
}

export async function uploadToNextcloud(file: File, category: string = 'general'): Promise<UploadResult> {
  const uploader = new NextcloudUploader();
  return await uploader.uploadFile(file, category);
}

export async function testNextcloudConnection(): Promise<{ success: boolean; error?: string; details?: any }> {
  const uploader = new NextcloudUploader();
  return await uploader.testConnection();
}

export function isNextcloudConfigured(): boolean {
  const uploader = new NextcloudUploader();
  return uploader.isConfigured();
}