import { Injectable, Logger } from '@nestjs/common';
import { NextcloudConnector, ConnectorHealth } from './nextcloud.connector';
import {
  NextcloudFile,
  NextcloudUser,
  NextcloudShare,
  NextcloudCalendar,
  NextcloudCalendarEvent,
  ListFilesDto,
  CreateShareDto,
  ListEventsDto,
  ShareType,
  SharePermission,
} from './nextcloud.types';

/**
 * Nextcloud Service
 *
 * High-level service for interacting with Nextcloud.
 * Wraps the connector with additional business logic and logging.
 */
@Injectable()
export class NextcloudService {
  private readonly logger = new Logger(NextcloudService.name);

  constructor(private readonly connector: NextcloudConnector) {}

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

  /**
   * Check health status of the Nextcloud integration
   */
  async checkHealth(): Promise<ConnectorHealth> {
    this.logger.debug('Checking Nextcloud connector health');
    return this.connector.checkHealth();
  }

  /**
   * Test connection to Nextcloud
   */
  async testConnection(): Promise<{ success: boolean; message: string; latency?: number }> {
    this.logger.log('Testing Nextcloud connection');
    const result = await this.connector.testConnection();
    this.logger.log(`Nextcloud connection test: ${result.success ? 'SUCCESS' : 'FAILED'} - ${result.message}`);
    return result;
  }

  /**
   * Check if connector is configured
   */
  isConfigured(): boolean {
    return this.connector.getIsConfigured();
  }

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

  /**
   * List files in a directory
   */
  async listFiles(params: ListFilesDto = {}): Promise<NextcloudFile[]> {
    const path = params.path || '/';
    this.logger.debug(`Listing files in: ${path}`);
    const files = await this.connector.listFiles(params);
    this.logger.debug(`Found ${files.length} files in ${path}`);
    return files;
  }

  /**
   * Get file/folder info
   */
  async getFileInfo(path: string): Promise<NextcloudFile> {
    this.logger.debug(`Getting file info: ${path}`);
    return this.connector.getFileInfo(path);
  }

  /**
   * Upload a file
   */
  async uploadFile(
    path: string,
    content: Buffer | string,
    contentType?: string,
  ): Promise<NextcloudFile> {
    this.logger.log(`Uploading file to: ${path}`);
    const file = await this.connector.uploadFile(path, content, contentType);
    this.logger.log(`File uploaded: ${path} (${file.size} bytes)`);
    return file;
  }

  /**
   * Download a file
   */
  async downloadFile(path: string): Promise<Buffer> {
    this.logger.debug(`Downloading file: ${path}`);
    const content = await this.connector.downloadFile(path);
    this.logger.debug(`Downloaded file: ${path} (${content.length} bytes)`);
    return content;
  }

  /**
   * Delete a file or folder
   */
  async deleteFile(path: string): Promise<void> {
    this.logger.log(`Deleting: ${path}`);
    await this.connector.deleteFile(path);
    this.logger.log(`Deleted: ${path}`);
  }

  /**
   * Create a folder
   */
  async createFolder(path: string): Promise<NextcloudFile> {
    this.logger.log(`Creating folder: ${path}`);
    const folder = await this.connector.createFolder(path);
    this.logger.log(`Created folder: ${path}`);
    return folder;
  }

  /**
   * Move a file or folder
   */
  async moveFile(source: string, destination: string, overwrite: boolean = false): Promise<void> {
    this.logger.log(`Moving ${source} to ${destination}`);
    await this.connector.moveFile(source, destination, overwrite);
    this.logger.log(`Moved ${source} to ${destination}`);
  }

  /**
   * Copy a file or folder
   */
  async copyFile(source: string, destination: string, overwrite: boolean = false): Promise<void> {
    this.logger.log(`Copying ${source} to ${destination}`);
    await this.connector.copyFile(source, destination, overwrite);
    this.logger.log(`Copied ${source} to ${destination}`);
  }

  /**
   * Ensure a folder exists (create if not)
   */
  async ensureFolder(path: string): Promise<NextcloudFile> {
    this.logger.debug(`Ensuring folder exists: ${path}`);

    try {
      const info = await this.connector.getFileInfo(path);
      if (info.type === 'directory') {
        return info;
      }
      throw new Error(`Path exists but is not a directory: ${path}`);
    } catch (error) {
      // Folder doesn't exist, create it
      return this.createFolder(path);
    }
  }

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

  /**
   * Get shares for a path
   */
  async getShares(path?: string): Promise<NextcloudShare[]> {
    this.logger.debug(`Getting shares${path ? ` for: ${path}` : ''}`);
    const shares = await this.connector.getShares(path);
    this.logger.debug(`Found ${shares.length} shares`);
    return shares;
  }

  /**
   * Create a share
   */
  async createShare(data: CreateShareDto): Promise<NextcloudShare> {
    this.logger.log(`Creating share for: ${data.path}`);
    const share = await this.connector.createShare(data);
    this.logger.log(`Created share ID: ${share.id}`);
    return share;
  }

  /**
   * Create a public link share
   */
  async createPublicLink(
    path: string,
    options?: {
      password?: string;
      expireDate?: string;
      permissions?: number;
    },
  ): Promise<NextcloudShare> {
    this.logger.log(`Creating public link for: ${path}`);
    const share = await this.connector.createShare({
      path,
      shareType: ShareType.PUBLIC_LINK,
      permissions: options?.permissions ?? SharePermission.READ,
      password: options?.password,
      expireDate: options?.expireDate,
    });
    this.logger.log(`Created public link: ${share.url}`);
    return share;
  }

  /**
   * Share with a user
   */
  async shareWithUser(
    path: string,
    userId: string,
    permissions: number = SharePermission.READ,
  ): Promise<NextcloudShare> {
    this.logger.log(`Sharing ${path} with user: ${userId}`);
    const share = await this.connector.createShare({
      path,
      shareType: ShareType.USER,
      shareWith: userId,
      permissions,
    });
    this.logger.log(`Shared with user ${userId}, share ID: ${share.id}`);
    return share;
  }

  /**
   * Delete a share
   */
  async deleteShare(shareId: number): Promise<void> {
    this.logger.log(`Deleting share: ${shareId}`);
    await this.connector.deleteShare(shareId);
    this.logger.log(`Deleted share: ${shareId}`);
  }

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

  /**
   * Get current user info
   */
  async getCurrentUser(): Promise<NextcloudUser> {
    this.logger.debug('Getting current user info');
    return this.connector.getCurrentUser();
  }

  /**
   * Get user quota
   */
  async getUserQuota(): Promise<{ used: number; total: number; free: number; relative: number }> {
    this.logger.debug('Getting user quota');
    return this.connector.getUserQuota();
  }

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

  /**
   * List calendars
   */
  async listCalendars(): Promise<NextcloudCalendar[]> {
    this.logger.debug('Listing calendars');
    const calendars = await this.connector.listCalendars();
    this.logger.debug(`Found ${calendars.length} calendars`);
    return calendars;
  }

  /**
   * List calendar events
   */
  async listEvents(params: ListEventsDto = {}): Promise<NextcloudCalendarEvent[]> {
    this.logger.debug(`Listing events for calendar: ${params.calendarId || 'personal'}`);
    const events = await this.connector.listEvents(params);
    this.logger.debug(`Found ${events.length} events`);
    return events;
  }

  /**
   * Get events for today
   */
  async getTodayEvents(calendarId?: string): Promise<NextcloudCalendarEvent[]> {
    const today = new Date();
    const startOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate());
    const endOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);

    return this.listEvents({
      calendarId,
      start: startOfDay.toISOString(),
      end: endOfDay.toISOString(),
    });
  }

  /**
   * Get events for a date range
   */
  async getEventsInRange(
    startDate: Date,
    endDate: Date,
    calendarId?: string,
  ): Promise<NextcloudCalendarEvent[]> {
    return this.listEvents({
      calendarId,
      start: startDate.toISOString(),
      end: endDate.toISOString(),
    });
  }
}
