import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ViewEncapsulation, forwardRef } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { IconDefinition } from '@fortawesome/fontawesome-svg-core';
import { faCloudUploadAlt, faFile, faImage, faFilePdf, faFileWord, faFileExcel, faFileCode, faTimes, faSpinner } from '@fortawesome/free-solid-svg-icons';

export type FileUploadSize = 'sm' | 'md' | 'lg';
export type FileUploadVariant = 'outlined' | 'filled' | 'underlined';
export type FileUploadState = 'default' | 'error' | 'success' | 'warning';

export interface UploadedFile {
  file: File;
  id: string;
  name: string;
  size: number;
  type: string;
  status: 'pending' | 'uploading' | 'completed' | 'error';
  progress?: number;
  error?: string;
  url?: string;
}

@Component({
  selector: 'ui-file-upload',
  standalone: true,
  imports: [CommonModule, FontAwesomeModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => FileUploadComponent),
      multi: true
    }
  ],
  template: `
    <div class="ui-file-upload" [class]="getWrapperClasses()">
      <!-- Label -->
      @if (label) {
        <label 
          class="ui-file-upload__label"
          [class.ui-file-upload__label--required]="required"
          [for]="inputId"
        >
          {{ label }}
        </label>
      }
      
      <!-- Dropzone -->
      <div 
        class="ui-file-upload__dropzone"
        [class.ui-file-upload__dropzone--dragover]="isDragOver"
        [attr.tabindex]="disabled ? -1 : 0"
        [attr.role]="'button'"
        [attr.aria-label]="'Upload files'"
        (click)="openFileDialog()"
        (dragover)="onDragOver($event)"
        (dragleave)="onDragLeave($event)"
        (drop)="onDrop($event)"
        (keydown)="onKeyDown($event)"
      >
        <fa-icon 
          class="ui-file-upload__icon" 
          [icon]="faCloudUploadAlt"
        ></fa-icon>
        
        <div class="ui-file-upload__text">
          <div class="ui-file-upload__text-primary">
            {{ isDragOver ? 'Drop files here' : 'Drag & drop files here' }}
          </div>
          <div class="ui-file-upload__text-secondary">
            @if (acceptedTypes.length > 0) {
              <div>Accepted formats: {{ getAcceptedTypesText() }}</div>
            }
            @if (maxFileSize) {
              <div>Max file size: {{ formatFileSize(maxFileSize) }}</div>
            }
            @if (maxFiles && maxFiles > 1) {
              <div>Max {{ maxFiles }} files</div>
            }
          </div>
        </div>
        
        <button 
          type="button" 
          class="ui-file-upload__browse-btn"
          [disabled]="disabled"
          (click)="$event.stopPropagation(); openFileDialog()"
        >
          Browse Files
        </button>
      </div>
      
      <!-- Hidden File Input -->
      <input
        #fileInput
        class="ui-file-upload__input"
        [id]="inputId"
        type="file"
        [accept]="accept"
        [multiple]="multiple"
        [disabled]="disabled"
        (change)="onFileSelected($event)"
      />
      
      <!-- File List -->
      @if (uploadedFiles.length > 0) {
        <div class="ui-file-upload__file-list">
          @for (uploadedFile of uploadedFiles; track uploadedFile.id) {
            <div 
              class="ui-file-upload__file-item"
              [class.ui-file-upload__file-item--uploading]="uploadedFile.status === 'uploading'"
              [class.ui-file-upload__file-item--error]="uploadedFile.status === 'error'"
              [class.ui-file-upload__file-item--success]="uploadedFile.status === 'completed'"
            >
              <div class="ui-file-upload__file-info">
                <fa-icon 
                  class="ui-file-upload__file-icon" 
                  [icon]="getFileIcon(uploadedFile.type)"
                ></fa-icon>
                <div class="ui-file-upload__file-details">
                  <div class="ui-file-upload__file-name" [title]="uploadedFile.name">
                    {{ uploadedFile.name }}
                  </div>
                  <div class="ui-file-upload__file-size">
                    {{ formatFileSize(uploadedFile.size) }}
                    @if (uploadedFile.status === 'error' && uploadedFile.error) {
                      <span> - {{ uploadedFile.error }}</span>
                    }
                  </div>
                </div>
              </div>
              
              <div class="ui-file-upload__file-actions">
                @if (uploadedFile.status === 'uploading') {
                  <fa-icon [icon]="faSpinner" [animation]="'spin'"></fa-icon>
                } @else {
                  <button
                    type="button"
                    class="ui-file-upload__remove-btn"
                    [attr.aria-label]="'Remove ' + uploadedFile.name"
                    (click)="removeFile(uploadedFile.id)"
                  >
                    <fa-icon [icon]="faTimes"></fa-icon>
                  </button>
                }
              </div>
              
              <!-- Progress Bar -->
              @if (uploadedFile.status === 'uploading' && uploadedFile.progress !== undefined) {
                <div 
                  class="ui-file-upload__progress"
                  [style.width.%]="uploadedFile.progress"
                ></div>
              }
            </div>
          }
        </div>
      }
      
      <!-- Helper Text -->
      @if (helperText || errorMessage) {
        <div class="ui-file-upload__helper-text" [class]="getHelperTextClasses()">
          {{ state === 'error' ? errorMessage : helperText }}
        </div>
      }
    </div>
  `,
  styleUrl: './file-upload.component.scss'
})
export class FileUploadComponent implements ControlValueAccessor {
  @Input() label: string = '';
  @Input() size: FileUploadSize = 'md';
  @Input() variant: FileUploadVariant = 'outlined';
  @Input() state: FileUploadState = 'default';
  @Input() disabled = false;
  @Input() required = false;
  @Input() multiple = false;
  @Input() accept: string = '';
  @Input() acceptedTypes: string[] = [];
  @Input() maxFileSize: number | null = null; // in bytes
  @Input() maxFiles: number | null = null;
  @Input() helperText: string = '';
  @Input() errorMessage: string = '';
  @Input() autoUpload = false;
  @Input() uploadUrl: string = '';
  @Input() inputId: string = `file-upload-${Math.random().toString(36).substr(2, 9)}`;

  @Output() filesSelected = new EventEmitter<UploadedFile[]>();
  @Output() fileAdded = new EventEmitter<UploadedFile>();
  @Output() fileRemoved = new EventEmitter<UploadedFile>();
  @Output() uploadProgress = new EventEmitter<{file: UploadedFile, progress: number}>();
  @Output() uploadComplete = new EventEmitter<UploadedFile>();
  @Output() uploadError = new EventEmitter<{file: UploadedFile, error: string}>();

  uploadedFiles: UploadedFile[] = [];
  isDragOver = false;

  // FontAwesome icons
  readonly faCloudUploadAlt = faCloudUploadAlt;
  readonly faFile = faFile;
  readonly faImage = faImage;
  readonly faFilePdf = faFilePdf;
  readonly faFileWord = faFileWord;
  readonly faFileExcel = faFileExcel;
  readonly faFileCode = faFileCode;
  readonly faTimes = faTimes;
  readonly faSpinner = faSpinner;

  // ControlValueAccessor implementation
  private onChange = (value: UploadedFile[]) => {};
  private onTouched = () => {};

  writeValue(value: UploadedFile[]): void {
    this.uploadedFiles = value || [];
  }

  registerOnChange(fn: (value: UploadedFile[]) => void): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  getWrapperClasses(): string {
    const classes = [
      `ui-file-upload--${this.size}`,
      `ui-file-upload--${this.variant}`,
      `ui-file-upload--${this.state}`
    ];

    if (this.disabled) classes.push('ui-file-upload--disabled');
    if (this.isDragOver) classes.push('ui-file-upload--dragover');

    return classes.join(' ');
  }

  getHelperTextClasses(): string {
    return `ui-file-upload__helper-text--${this.state}`;
  }

  openFileDialog(): void {
    if (!this.disabled) {
      const input = document.getElementById(this.inputId) as HTMLInputElement;
      input?.click();
    }
  }

  onFileSelected(event: Event): void {
    const input = event.target as HTMLInputElement;
    if (input.files) {
      this.handleFiles(Array.from(input.files));
      // Reset input value to allow selecting the same file again
      input.value = '';
    }
  }

  onDragOver(event: DragEvent): void {
    if (!this.disabled) {
      event.preventDefault();
      event.stopPropagation();
      this.isDragOver = true;
    }
  }

  onDragLeave(event: DragEvent): void {
    if (!this.disabled) {
      event.preventDefault();
      event.stopPropagation();
      this.isDragOver = false;
    }
  }

  onDrop(event: DragEvent): void {
    if (!this.disabled) {
      event.preventDefault();
      event.stopPropagation();
      this.isDragOver = false;

      const files = event.dataTransfer?.files;
      if (files) {
        this.handleFiles(Array.from(files));
      }
    }
  }

  onKeyDown(event: KeyboardEvent): void {
    if ((event.key === 'Enter' || event.key === ' ') && !this.disabled) {
      event.preventDefault();
      this.openFileDialog();
    }
  }

  handleFiles(files: File[]): void {
    let validFiles: File[] = files;

    // Filter by accepted types
    if (this.acceptedTypes.length > 0) {
      validFiles = files.filter(file => this.isFileTypeAccepted(file));
    }

    // Filter by file size
    if (this.maxFileSize) {
      validFiles = validFiles.filter(file => file.size <= this.maxFileSize!);
    }

    // Limit number of files
    if (this.maxFiles) {
      const remainingSlots = this.maxFiles - this.uploadedFiles.length;
      validFiles = validFiles.slice(0, remainingSlots);
    }

    // Create UploadedFile objects
    const newUploadedFiles: UploadedFile[] = validFiles.map(file => ({
      file,
      id: this.generateFileId(),
      name: file.name,
      size: file.size,
      type: file.type,
      status: 'pending',
      progress: 0
    }));

    // Add to uploaded files array
    if (this.multiple) {
      this.uploadedFiles = [...this.uploadedFiles, ...newUploadedFiles];
    } else {
      this.uploadedFiles = newUploadedFiles;
    }

    // Emit events
    newUploadedFiles.forEach(uploadedFile => {
      this.fileAdded.emit(uploadedFile);
    });

    this.filesSelected.emit(this.uploadedFiles);
    this.onChange(this.uploadedFiles);
    this.onTouched();

    // Auto upload if enabled
    if (this.autoUpload && this.uploadUrl) {
      newUploadedFiles.forEach(uploadedFile => {
        this.uploadFile(uploadedFile);
      });
    }
  }

  removeFile(fileId: string): void {
    const fileIndex = this.uploadedFiles.findIndex(f => f.id === fileId);
    if (fileIndex > -1) {
      const removedFile = this.uploadedFiles[fileIndex];
      this.uploadedFiles.splice(fileIndex, 1);
      this.uploadedFiles = [...this.uploadedFiles]; // Trigger change detection
      
      this.fileRemoved.emit(removedFile);
      this.onChange(this.uploadedFiles);
    }
  }

  private uploadFile(uploadedFile: UploadedFile): void {
    uploadedFile.status = 'uploading';
    
    const formData = new FormData();
    formData.append('file', uploadedFile.file);

    // Simulate upload progress (replace with actual upload logic)
    let progress = 0;
    const interval = setInterval(() => {
      progress += 10;
      uploadedFile.progress = progress;
      this.uploadProgress.emit({ file: uploadedFile, progress });

      if (progress >= 100) {
        clearInterval(interval);
        uploadedFile.status = 'completed';
        this.uploadComplete.emit(uploadedFile);
      }
    }, 200);
  }

  private isFileTypeAccepted(file: File): boolean {
    return this.acceptedTypes.some(type => {
      if (type.startsWith('.')) {
        return file.name.toLowerCase().endsWith(type.toLowerCase());
      }
      return file.type.includes(type);
    });
  }

  private generateFileId(): string {
    return `file-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
  }

  getFileIcon(fileType: string): IconDefinition {
    if (fileType.startsWith('image/')) return this.faImage;
    if (fileType.includes('pdf')) return this.faFilePdf;
    if (fileType.includes('word') || fileType.includes('document')) return this.faFileWord;
    if (fileType.includes('excel') || fileType.includes('spreadsheet')) return this.faFileExcel;
    if (fileType.includes('code') || fileType.includes('javascript') || fileType.includes('typescript')) return this.faFileCode;
    return this.faFile;
  }

  formatFileSize(bytes: number): string {
    if (bytes === 0) return '0 Bytes';
    const k = 1024;
    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }

  getAcceptedTypesText(): string {
    return this.acceptedTypes.join(', ').toUpperCase();
  }
}