import { autoinject, customAttribute, TaskQueue } from 'aurelia-framework';
import { Color, color } from '../utils/color';

@autoinject
@customAttribute("ui-theme-observer")
export class UIThemeObserverAttribute {
  private mutationObserver: MutationObserver;
  private oldPrimaryColor: string;
  private oldSecondaryColor: string;

  private whiteColor: Color;
  private blackColor: Color;
  private baseTheme: Color;
  private primaryColor: Color;
  private baseComponentBg: Color;
  private secondaryColor: Color;
  private baseInvert: Color;
  private menuHoverBg: Color;
  private btnBg: Color;
  private tertiaryColor: Color;

  private overridableVariables: Array<OverridableVariable>;

  constructor(private taskQueue: TaskQueue) {
    this.whiteColor = color("white");
    this.blackColor = color("black");
    this.overridableVariables = new Array();
  }

  public attached() {
    let documentStyle = getComputedStyle(document.documentElement);

    this.primaryColor = color(documentStyle.getPropertyValue("--primary")?.trim() ?? this.whiteColor);
    this.secondaryColor = color(documentStyle.getPropertyValue("--secondary")?.trim() ?? this.whiteColor);
    this.baseTheme = color(documentStyle.getPropertyValue("--base-theme")?.trim() ?? this.whiteColor);
    this.baseInvert = color(documentStyle.getPropertyValue("--base-invert")?.trim() ?? this.blackColor);
    this.baseComponentBg = color(documentStyle.getPropertyValue("--base-component-bg")?.trim() ?? this.whiteColor);
    this.menuHoverBg = this.baseTheme.mix(this.primaryColor, 0.5);

    this.overridableVariables.push(new OverridableVariable("sidebar-resizer", () => { return this.baseTheme.mix(this.primaryColor, 0.7).hex() }));
    this.overridableVariables.push(new OverridableVariable("scrollbar-bg", () => { return this.baseComponentBg.mix(this.primaryColor, 0.25).hex() }));
    this.overridableVariables.push(new OverridableVariable("scrollbar-bg-hover", () => { return this.baseTheme.mix(this.primaryColor, 0.6).hex() }));

    this.overridableVariables.push(new OverridableVariable("menu-hover-bg", () => { return this.menuHoverBg.hex() }));
    this.overridableVariables.push(new OverridableVariable("menu-hover-color", () => { return this.menuHoverBg.textColor().hex() }));
    this.overridableVariables.push(new OverridableVariable("menu-active-color", () => { return this.primaryColor.hex() }));
    this.overridableVariables.push(new OverridableVariable("menu-active-border-color", () => { return this.blackColor.mix("gray", 0.05).mix(this.primaryColor, 0.5).hex() }));

    this.overridableVariables.push(new OverridableVariable("divider-bg", () => { return this.primaryColor.alpha(0.5).hex("rgba") }));

    this.overridableVariables.push(new OverridableVariable("tree-hover-bg", () => { return this.baseTheme.mix(this.secondaryColor, 0.5).hex() }));

    this.overridableVariables.push(new OverridableVariable("tooltip-bg", () => { return this.baseTheme.mix(this.primaryColor, 0.7).hex() }));

    this.overridableVariables.push(new OverridableVariable("dlg-header-active-bg", () => { return this.baseTheme.mix(this.primaryColor, 0.8).hex() }));

    this.overridableVariables.push(new OverridableVariable("input-focus-border-color", () => { return this.baseTheme.mix(this.secondaryColor, 0.25).hex() }));

    this.overridableVariables.push(new OverridableVariable("list-item-hover-bg", () => { return this.primaryColor.alpha(0.15).hex("rgba") }));
    this.overridableVariables.push(new OverridableVariable("list-item-hover-color", () => { return this.primaryColor.hex() }));
    this.overridableVariables.push(new OverridableVariable("list-item-active-bg", () => { return this.primaryColor.hex() }));
    this.overridableVariables.push(new OverridableVariable("list-item-active-color", () => { return this.primaryColor.textColor().hex() }));

    this.overridableVariables.push(new OverridableVariable("ux-dialog-background-header", () => { return this.primaryColor.hex() }));
    this.overridableVariables.push(new OverridableVariable("ux-dialog-color-header", () => { return this.primaryColor.textColor().hex() }));

    for (const overridableVariable of this.overridableVariables) {
      try {
        let actualColor = color(documentStyle.getPropertyValue("--" + overridableVariable.variableNameCSS)?.trim());
        overridableVariable.colorOverride = actualColor.hex();
      }
      catch {

      }
    }

    this.taskQueue.queueMicroTask(() => {
      this.mutationObserver = new MutationObserver(() => this.themeChanged());
      this.mutationObserver.observe(document.documentElement, {
        "attributes": true,
        "attributeFilter": ["style"]
      });
    });
    this.themeChanged(true);
  }

  public detached() {
    this.mutationObserver?.disconnect();
  }

  /**
   * Triggered when style change on document.documentElement and when primary or secondary value has changed
   * 
   */
  public themeChanged(force: boolean = false) {
    try {
      let documentStyle = getComputedStyle(document.documentElement);
      let primary = documentStyle.getPropertyValue("--primary")?.trim();
      let secondary = documentStyle.getPropertyValue("--secondary")?.trim();
      if (force || (this.oldPrimaryColor != primary && this.oldSecondaryColor != secondary)) {
        this.primaryColor = color(primary ?? this.whiteColor);
        this.secondaryColor = color(secondary ?? this.whiteColor);
        this.tertiaryColor = this.blackColor.mix(this.primaryColor, 0.5);
        document.body.style.setProperty("--tertiary", this.tertiaryColor.hex());

        this.baseTheme = color(documentStyle.getPropertyValue("--base-theme")?.trim() ?? this.whiteColor);
        this.baseInvert = color(documentStyle.getPropertyValue("--base-invert")?.trim() ?? this.blackColor);

        this.baseComponentBg = color(documentStyle.getPropertyValue("--base-component-bg")?.trim() ?? this.whiteColor);

        this.menuHoverBg = this.baseTheme.mix(this.primaryColor, 0.5);

        let themes = [{ name: "primary", color: this.primaryColor }, { name: "secondary", color: this.secondaryColor }, { name: "tertiary", color: this.tertiaryColor }];

        document.body.style.setProperty("--primary-reverse", this.primaryColor.textColor().hex());
        document.body.style.setProperty("--secondary-reverse", this.secondaryColor.textColor().hex());
        document.body.style.setProperty("--tertiary-reverse", this.tertiaryColor.textColor().hex());

        this.btnBg = color(documentStyle.getPropertyValue("--btn-bg")?.trim() ?? this.whiteColor);
        let setVaribleByTheme = (color: Color, colorName: string) => {
          document.body.style.setProperty("--" + colorName + "-base-theme-10", this.baseTheme.mix(color, 0.1).hex());
          document.body.style.setProperty("--" + colorName + "-white-10", this.whiteColor.mix(color, 0.1).hex());
          document.body.style.setProperty("--" + colorName + "-white-50", this.whiteColor.mix(color, 0.5).hex());
          document.body.style.setProperty("--" + colorName + "-white-75", this.whiteColor.mix(color, 0.75).hex());
          document.body.style.setProperty("--" + colorName + "-black-40", this.blackColor.mix(color, 0.4).hex());
          document.body.style.setProperty("--" + colorName + "-btn-bg-90", this.btnBg.mix(color, 0.9).hex());
        };

        for (let theme of themes) {
          setVaribleByTheme(theme.color, theme.name);
        }

        for (const overridableVariable of this.overridableVariables) {
          document.body.style.setProperty("--" + overridableVariable.variableNameCSS, overridableVariable.colorOverride ?? overridableVariable.color());
        }

        for (let index = 5; index < 100; index += 5) {
          let indexString = index.toLocaleString(undefined, {
            minimumIntegerDigits: 2,
          });
          for (let theme of themes) {
            document.body.style.setProperty("--" + theme.name + "-" + indexString, theme.color.alpha(index / 100).hex("rgba"));
            document.body.style.setProperty("--" + theme.name + "-lighten-" + indexString, theme.color.brighter(index / 100).hex("rgba"));
            document.body.style.setProperty("--" + theme.name + "-darken-" + indexString, theme.color.darker(index / 100).hex("rgba"));
          }
        }
      }

      this.taskQueue.queueTask(() => {
        this.oldPrimaryColor = primary;
        this.oldSecondaryColor = secondary;
      });
    }
    catch { }
  }
}

export class OverridableVariable {
  variableNameCSS: string;
  colorOverride: string;
  color: () => string;
  constructor(variableNameCSS: string, color: () => string) {
    this.variableNameCSS = variableNameCSS;
    this.color = color;
  }
}