/**
 * 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-preview.css';
import NileElement from '../internal/nile-element';
import { customElement, property } from 'lit/decorators.js';
import { generatePreviewUrl, isRealImage } from './utils';
import { FilePreviewState, FilePreviewVariant, FilePreviewErrorMessages, FilePreviewEvent } from './types';
import { LitElement, html, CSSResultArray, TemplateResult, PropertyValues } from 'lit';
import {
  getHorizontalErrorState,
  getHorizontalNoPreviewState,
  getHorizontalPreviewState,
  getHorizontalUploadingState,
  getVerticalErrorState,
  getVerticalNoPreviewState,
  getVerticalPreviewState,
  getVerticalUploadingState
} from './nile-file-preview.template';


/**
 * Nile preview component.
 *
 * @tag nile-preview
 *
 */
@customElement('nile-file-preview')
export class NileFilePreview extends NileElement {
  /**
   * The styles for nile-preview
   * @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 }) errorMessage: string = "";
	@property({ type: Object }) inputFile: File;
	@property({ type: String }) fileUrl: string = "";
	@property({ type: Number }) uploadStatus: number = 0;
	@property({ type: String }) state: FilePreviewState = FilePreviewState.DEFAULT;
	@property({ type: String }) variant: FilePreviewVariant = FilePreviewVariant.HORIZONTAL;
  @property({ type: String }) inputFileName: string = "";
  @property({ type: Boolean, reflect: true, attribute: true }) showFileType: boolean = false;
	private inputFileHtml: TemplateResult | null = null;
  private originalUrl: string = "";
  public isStringTruncated: boolean = false;

  /**
   * Render method
   * @slot This is a slot test
   */

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

  protected firstUpdated(_changedProperties: PropertyValues): void {
    super.firstUpdated(_changedProperties);
  }

  protected updated(changedProperties: PropertyValues): void {
    if(changedProperties.has('uploadStatus') && this.uploadStatus) {
      if(this.uploadStatus < 100) {
        this.createUploadState(this.inputFile, this.uploadStatus);
      } else {
        this.createState(this.inputFile);
      }
    } else if (changedProperties.has('inputFile') && this.inputFile) {
      this.createState(this.inputFile);
      this.emit(FilePreviewEvent.NILE_RECEIVE);

    } else if (changedProperties.has('fileUrl') && this.fileUrl) {
      this.handleFileUrl(this.fileUrl);
      this.emit(FilePreviewEvent.NILE_RECEIVE);

    } else if (changedProperties.has('errorMessage')) {
      this.createState(this.inputFile);
    }
  }

  private createUploadState( file: File, uploadStatus: number): void {
    const isHorizontal = this.variant === FilePreviewVariant.HORIZONTAL;

    this.inputFileHtml = isHorizontal
    ? getHorizontalUploadingState(file, uploadStatus, this)
    : getVerticalUploadingState(file, uploadStatus, this);

    this.requestUpdate();
  }

  private async handleFileUrl(url: string): Promise<void> {
    this.originalUrl = url;
    try {
      const response = await fetch(url);
      const blob = await response.blob();
      const fileName = this.inputFileName || url.split('/').pop() || FilePreviewErrorMessages.UNKNOWN_FILE_NAME;
      const file = new File([blob], fileName, { type: blob.type });

      this.createState(file);
    } catch (error) {
      const dummyFile = new File([], this.inputFileName || FilePreviewErrorMessages.UNKNOWN_FILE_NAME, { type: 'application/octet-stream' });
      this.errorMessage = FilePreviewErrorMessages.FAILED_TO_LOAD;
      console.error(this.errorMessage);

      this.inputFileHtml = this.variant === FilePreviewVariant.HORIZONTAL
        ? getHorizontalErrorState(dummyFile, this.errorMessage, this.originalUrl, this)
        : getVerticalErrorState(dummyFile, this.errorMessage, this.originalUrl, this);
      this.requestUpdate();
    }
  }

	public async createState(file: File): Promise<void> {
    const isHorizontal = this.variant === FilePreviewVariant.HORIZONTAL;

    if (!this.errorMessage) {
      let isImage = false;
      try {
        isImage = file && await isRealImage(file);
      } catch (error) {
        isImage = false;
      }
      if (isImage) {
        try {
          const url = await generatePreviewUrl(file);
          this.inputFileHtml = isHorizontal
            ? getHorizontalPreviewState(url, file, this.originalUrl, this.showFileType)
            : getVerticalPreviewState(url, file, this.originalUrl, this.showFileType);
        } catch (error) {
          this.inputFileHtml = isHorizontal
            ? getHorizontalNoPreviewState(file, this.originalUrl, this.showFileType)
            : getVerticalNoPreviewState(file, this.originalUrl, this.showFileType);
        }
      } else {
        this.inputFileHtml = isHorizontal
          ? getHorizontalNoPreviewState(file, this.originalUrl, this.showFileType)
          : getVerticalNoPreviewState(file, this.originalUrl, this.showFileType);
      }
    } else {
      this.inputFileHtml = isHorizontal
        ? getHorizontalErrorState(file, this.errorMessage, this.originalUrl, this)
        : getVerticalErrorState(file, this.errorMessage, this.originalUrl, this);
    }
    this.requestUpdate();
  }

  public render(): TemplateResult {
    return html`${this.inputFileHtml ?? html``}`;
  }


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

  /* #endregion */
}

export default NileFilePreview;

declare global {
  interface HTMLElementTagNameMap {
    'nile-file-preview': NileFilePreview;
  }
}
