/**
 * Copyright Aquera Inc 2025
 *
 * This source code is licensed under the BSD-3-Clause license found in the
 * LICENSE file in the root directory of this source tree.
 */

import { styles } from './nile-file-upload.css';
import { ChangedPropsInterface } from './types/file-upload.type'
import NileElement from '../internal/nile-element';
import { DragHandler } from './utils/drag-drop.util';
import { FileUploadDefaults } from './types/file-upload.enums';
import { LitElement, html, CSSResultArray, TemplateResult } from 'lit';
import { customElement, property, query, state } from 'lit/decorators.js';
import { FileUploadState, FileUploadVariant, FileUploadError, FileUploadEvent } from './types/file-upload.enums';
import { setUpDragHandler, addGlobalListeners, addInternalListeners, uploadFiles, cancelFileUpload } from './utils/file-validation.util';
import {
  getHorizontalDefaultState,
  getHorizontalDragState,
  getVerticalDefaultState,
  getVerticalDisabledState,
  getVerticalDragState,
  getHorizontalErrorState,
  getVerticalErrorState,
  getHorizontalDisabledState
} from './nile-file-upload.template';

/**
 * Nile file-upload component.
 *
 * @tag nile-file-upload
 *
 */
@customElement('nile-file-upload')
export class NileFileUpload extends NileElement {
  /**
   * The styles for nile-file-upload
   * @remarks If you are extending this class you can extend the base styles with super. Eg `return [super(), myCustomStyles]`
   */
  public static get styles(): CSSResultArray {
    return [styles];
  }

  @property({ type: String }) size: string = "10MB";
  @property({ type: Boolean}) error: boolean = false;
  @property({ type: String }) errorMessage: string = "";
  @property({ type: Boolean}) allowMultiple: boolean = false;
  @property({ type: Boolean}) allowDuplicates: boolean = false;
  @property({ type: Array  }) allowedTypes: string[] = [];
  @property({ type: Array  }) uploadedFiles: File[] = [];
  @property({ type: String }) title: string = FileUploadDefaults.TITLE;
  @property({ type: String }) subtitle: string = FileUploadDefaults.SUBTITLE;
  @property({ type: String }) state: FileUploadState = FileUploadState.DEFAULT;
  @property({ type: String }) variant: FileUploadVariant = FileUploadVariant.HORIZONTAL;
  @property({ type: String }) fileUploadUrl: string = "";
  @property({ type: Boolean}) autoUpload: boolean = true;
  @state() public doNotUpload: File[] = [];
  @query('input') input!: HTMLInputElement;
  @query('.horizontal-div') horizontalDiv!: HTMLDivElement;
  public uploadRequests = new Map<File, XMLHttpRequest>();
  public fileSizeExceededFilesNumber: number = 0;
  public isStringTruncated: boolean = false;

  private dragHandler!: DragHandler;

  connectedCallback(): void {
    super.connectedCallback();
    this.emit(FileUploadEvent.NILE_INIT);
  }

  firstUpdated(changedProps: ChangedPropsInterface): void {
    super.firstUpdated(changedProps);

    this.dragHandler = new DragHandler();
    setUpDragHandler(this, this.dragHandler);
    addInternalListeners(this, this.dragHandler, this.uploadRequests);
    addGlobalListeners(this.dragHandler);
  }

  updated(changedProps: ChangedPropsInterface): void {
    super.updated(changedProps);

    if (changedProps.has('uploadedFiles')) {
      if(this.fileUploadUrl && this.autoUpload) {
        uploadFiles(this);
      }
      this.emit(FileUploadEvent.NILE_CHANGE, { files: this.uploadedFiles });
    } else if (changedProps.has('state')) {
      this.dragHandler.setValuesInDragHandler(this.state);
    }

    if(changedProps.has('autoUpload') && this.autoUpload) {
      if(this.fileUploadUrl && this.autoUpload) {
        uploadFiles(this);
      }
      this.emit(FileUploadEvent.NILE_CHANGE, { files: this.uploadedFiles });
    }
  }

  public browseFiles(): void {
    this.emit(FileUploadEvent.NILE_BROWSE);
    this.errorMessage = "";
    this.input.click();
  }

  public setState = (newState: FileUploadState): void => {
    this.state = newState;
  }

  render(): TemplateResult {
    return html`
      ${this.getState()}
    `;
  }

  public clearAllUploadedFiles(): void {
    this.uploadedFiles = [];
    this.state = FileUploadState.DEFAULT;
    this.errorMessage = "";
  }

  private getState(): TemplateResult {
    return this.variant === FileUploadVariant.HORIZONTAL
      ? this.getHorizontalState()
      : this.getVerticalState();
  }

  private getHorizontalState(): TemplateResult {
    switch(this.state) {
      case FileUploadState.DEFAULT:
        return getHorizontalDefaultState(this.browseFiles, this.title, this.subtitle, this.dragHandler, this.state, this.errorMessage, this.allowedTypes, this);

      case FileUploadState.DISABLED:
        return getHorizontalDisabledState(this.browseFiles, this.title, this.subtitle, this.dragHandler, this.state, this.errorMessage, this.allowedTypes, this);

      case FileUploadState.DRAG:
        return getHorizontalDragState();

      case FileUploadState.ERROR:
        return getHorizontalErrorState(this.browseFiles, this.title, this.subtitle, this.dragHandler, this.state, this.errorMessage, this.allowedTypes, this);

      default:
        return getHorizontalDefaultState(this.browseFiles, this.title, this.subtitle, this.dragHandler, this.state, this.errorMessage, this.allowedTypes, this);
    }
  }

  private getVerticalState(): TemplateResult {
    switch(this.state) {
      case FileUploadState.DEFAULT:
        return getVerticalDefaultState(this.browseFiles, this.title, this.subtitle, this.dragHandler, this.errorMessage, this.allowedTypes, this);

      case FileUploadState.DISABLED:
        return getVerticalDisabledState(this.browseFiles, this.title, this.subtitle, this.dragHandler, this.errorMessage, this.allowedTypes, this);

      case FileUploadState.DRAG:
        return getVerticalDragState();

      case FileUploadState.ERROR:
        return getVerticalErrorState(this.browseFiles, this.title, this.subtitle, this.dragHandler, this.errorMessage, this.allowedTypes, this);

      default:
        return getVerticalDefaultState(this.browseFiles, this.title, this.subtitle, this.dragHandler, this.errorMessage, this.allowedTypes, this);
    }
  }

  disconnectedCallback(): void {
    this.emit(FileUploadEvent.NILE_DESTROY);
  }
}
