import { computed, effect, Injectable, signal, TemplateRef } from '@angular/core';
import { MenuItem } from 'primeng/api';
import { BehaviorSubject, Subject } from 'rxjs';

export interface layoutConfig {
  preset?: string;
  primary?: string;
  surface?: string | undefined | null;
  darkTheme?: boolean;
  menuMode?: string;
}

interface IPanelInfo {
  title: string;
}

interface LayoutState {
  fullPageLoading?: boolean;
  staticMenuDesktopInactive?: boolean;
  overlayMenuActive?: boolean;
  configSidebarVisible?: boolean;
  staticMenuMobileActive?: boolean;
  menuHoverActive?: boolean;
  isFixedContentSize?: boolean;
  isFullPage?: boolean;
}

interface MenuChangeEvent {
  key: string;
  routeEvent?: boolean;
}

@Injectable({
  providedIn: 'root',
})
export class LayoutService {
  _config: layoutConfig = {
    preset: 'Aura',
    primary: 'surface',
    surface: null,
    darkTheme: localStorage.getItem('isDarkTheme') === 'true',
    menuMode: 'static',
  };

  _state: LayoutState = {
    fullPageLoading: false,
    staticMenuDesktopInactive: false,
    overlayMenuActive: false,
    configSidebarVisible: false,
    staticMenuMobileActive: false,
    menuHoverActive: false,
    isFixedContentSize: true,
    isFullPage: false,
  };

  layoutConfig = signal<layoutConfig>(this._config);

  layoutState = signal<LayoutState>(this._state);

  private configUpdate = new Subject<layoutConfig>();

  private overlayOpen = new Subject<any>();

  private menuSource = new Subject<MenuChangeEvent>();

  private resetSource = new Subject();

  public menuItems = signal<MenuItem[]>([]);

  public panelInfo = signal<IPanelInfo>({
    title: 'پنل مدیریت صورت‌حساب‌های مالیاتی',
  });

  menuSource$ = this.menuSource.asObservable();

  resetSource$ = this.resetSource.asObservable();

  configUpdate$ = this.configUpdate.asObservable();

  overlayOpen$ = this.overlayOpen.asObservable();

  theme = computed(() => (this.layoutConfig()?.darkTheme ? 'light' : 'dark'));

  isSidebarActive = computed(
    () => this.layoutState().overlayMenuActive || this.layoutState().staticMenuMobileActive,
  );

  isDarkTheme = computed(() => this.layoutConfig().darkTheme);

  getPrimary = computed(() => this.layoutConfig().primary);

  getSurface = computed(() => this.layoutConfig().surface);

  isOverlay = computed(() => this.layoutConfig().menuMode === 'overlay');

  isFixedContentSize = computed(() => this.layoutState().isFixedContentSize);
  isFullPage = computed(() => this.layoutState().isFullPage);

  transitionComplete = signal<boolean>(false);

  private initialized = false;

  constructor() {
    this.handleDarkModeTransition(this._config);
    effect(() => {
      const config = this.layoutConfig();
      if (config) {
        this.onConfigUpdate();
      }
    });

    effect(() => {
      const config = this.layoutConfig();

      if (!this.initialized || !config) {
        this.initialized = true;
        return;
      }

      this.handleDarkModeTransition(config);
    });
  }

  private handleDarkModeTransition(config: layoutConfig): void {
    if ((document as any).startViewTransition) {
      this.startViewTransition(config);
    } else {
      this.toggleDarkMode(config);
      this.onTransitionEnd();
    }
  }

  private startViewTransition(config: layoutConfig): void {
    const transition = (document as any).startViewTransition(() => {
      this.toggleDarkMode(config);
    });

    transition.ready
      .then(() => {
        this.onTransitionEnd();
      })
      .catch(() => {});
  }

  changeIsFixedContentSize(status: boolean) {
    this.layoutState.update((prev) => ({
      ...prev,
      isFixedContentSize: status,
    }));
  }

  changeIsFullPage(status: boolean) {
    this.layoutState.update((prev) => ({
      ...prev,
      isFullPage: status,
    }));
  }

  changeFullPageLoading(status: boolean) {
    this.layoutState.update((prev) => ({
      ...prev,
      fullPageLoading: status,
    }));
  }

  toggleDarkMode(config?: layoutConfig): void {
    const _config = config || this.layoutConfig();
    localStorage.setItem('isDarkTheme', JSON.stringify(_config.darkTheme));
    if (_config.darkTheme) {
      document.documentElement.classList.add('app-dark');
    } else {
      document.documentElement.classList.remove('app-dark');
    }
  }

  private onTransitionEnd() {
    this.transitionComplete.set(true);
    setTimeout(() => {
      this.transitionComplete.set(false);
    });
  }

  onMenuToggle() {
    if (this.isOverlay()) {
      this.layoutState.update((prev) => ({
        ...prev,
        overlayMenuActive: !this.layoutState().overlayMenuActive,
      }));

      if (this.layoutState().overlayMenuActive) {
        this.overlayOpen.next(null);
      }
    }

    if (this.isDesktop()) {
      this.layoutState.update((prev) => ({
        ...prev,
        staticMenuDesktopInactive: !this.layoutState().staticMenuDesktopInactive,
      }));
    } else {
      this.layoutState.update((prev) => ({
        ...prev,
        staticMenuMobileActive: !this.layoutState().staticMenuMobileActive,
      }));

      if (this.layoutState().staticMenuMobileActive) {
        this.overlayOpen.next(null);
      }
    }
  }

  isDesktop() {
    return window.innerWidth > 991;
  }

  isMobile() {
    return !this.isDesktop();
  }

  onConfigUpdate() {
    this._config = { ...this.layoutConfig() };
    this.configUpdate.next(this.layoutConfig());
  }

  onMenuStateChange(event: MenuChangeEvent) {
    this.menuSource.next(event);
  }

  reset() {
    this.resetSource.next(true);
  }

  setMenuItems(items: MenuItem[]) {
    this.menuItems.set(items);
  }

  setPanelInfo(info: IPanelInfo) {
    this.panelInfo.set(info);
  }

  private headerSlot = new BehaviorSubject<TemplateRef<any> | null>(null);
  headerSlot$ = this.headerSlot.asObservable();
  private topbarStartSlot = new BehaviorSubject<TemplateRef<any> | null>(null);
  topbarStartSlot$ = this.topbarStartSlot.asObservable();
  private topbarCenterSlot = new BehaviorSubject<TemplateRef<any> | null>(null);
  topbarCenterSlot$ = this.topbarCenterSlot.asObservable();
  private topbarEndSlot = new BehaviorSubject<TemplateRef<any> | null>(null);
  topbarEndSlot$ = this.topbarEndSlot.asObservable();

  setHeaderSlot(tpl: TemplateRef<any> | null) {
    // Backward-compatible alias for topbar end slot.
    this.setTopbarEndSlot(tpl);
    this.headerSlot.next(tpl);
  }

  setTopbarStartSlot(tpl: TemplateRef<any> | null) {
    this.topbarStartSlot.next(tpl);
  }

  setTopbarCenterSlot(tpl: TemplateRef<any> | null) {
    this.topbarCenterSlot.next(tpl);
  }

  setTopbarEndSlot(tpl: TemplateRef<any> | null) {
    this.topbarEndSlot.next(tpl);
  }
}
