import { DeesElement, property, html, customElement, type TemplateResult, queryAsync, render, domtools } from '@design.estate/dees-element';
import { resolveTemplateFactory, getDemoAtIndex, getDemoCount, hasMultipleDemos } from './wcctools.helpers.js';
import type { TTemplateFactory } from './wcctools.helpers.js';
import type { IWccConfig, IWccSection, TElementType } from '../wcctools.interfaces.js';

import * as plugins from '../wcctools.plugins.js';

// wcc tools
import './wcc-frame.js';
import './wcc-sidebar.js';
import './wcc-properties.js';
import { type TTheme } from './wcc-properties.js';
import { breakpoints } from '@design.estate/dees-domtools';
import { WccFrame } from './wcc-frame.js';
import { WccSidebar } from './wcc-sidebar.js';

/**
 * Get filtered and sorted items from a section
 */
export const getSectionItems = (section: IWccSection): Array<[string, any]> => {
  let entries = Object.entries(section.items);

  // Apply filter if provided
  if (section.filter) {
    const filterFn = section.filter;
    entries = entries.filter(([name, item]) => filterFn(name, item));
  }

  // Apply sort if provided
  if (section.sort) {
    entries.sort(section.sort);
  }

  return entries;
};

@customElement('wcc-dashboard')
export class WccDashboard extends DeesElement {

  @property()
  accessor sections: IWccSection[] = [];

  @property()
  accessor selectedSection: IWccSection | null = null;

  @property()
  accessor selectedType: TElementType = 'element';

  @property()
  accessor selectedItemName: string = '';

  @property()
  accessor selectedItem: TTemplateFactory | DeesElement | null = null;

  @property({ type: Number })
  accessor selectedDemoIndex: number = 0;

  @property()
  accessor selectedViewport: plugins.deesDomtools.breakpoints.TViewport = 'desktop';

  @property()
  accessor selectedTheme: TTheme = 'dark';

  @property()
  accessor searchQuery: string = '';

  // Pinned items as Set of "sectionName::itemName"
  @property({ attribute: false })
  accessor pinnedItems: Set<string> = new Set();

  // Sidebar width (resizable)
  @property({ type: Number })
  accessor sidebarWidth: number = 200;

  // Derived from selectedViewport - no need for separate property
  public get isNative(): boolean {
    return this.selectedViewport === 'native';
  }

  @property()
  accessor warning: string | null = null;

  private frameScrollY: number = 0;
  private sidebarScrollY: number = 0;
  private scrollPositionsApplied: boolean = false;

  @queryAsync('wcc-frame')
  accessor wccFrame: Promise<WccFrame>;

  constructor(config?: IWccConfig) {
    super();
    if (config && config.sections) {
      this.sections = config.sections;
      console.log('got sections:', this.sections.map(s => s.name));
    }
  }

  /**
   * Find an item by name across all sections, returns the item and its section
   */
  public findItemByName(name: string): { item: any; section: IWccSection } | null {
    for (const section of this.sections) {
      const entries = getSectionItems(section);
      const found = entries.find(([itemName]) => itemName === name);
      if (found) {
        return { item: found[1], section };
      }
    }
    return null;
  }

  /**
   * Find a section by name (URL-decoded)
   */
  public findSectionByName(name: string): IWccSection | null {
    return this.sections.find(s => s.name === name) || null;
  }

  public render(): TemplateResult {
    return html`
      <style>
        :host {
          background: #fcfcfc;
          display: block;
          box-sizing: border-box;
        }
        :host([hidden]) {
          display: none;
        }
      </style>
      <wcc-sidebar
        .dashboardRef=${this}
        .selectedItem=${this.selectedItem}
        .searchQuery=${this.searchQuery}
        .pinnedItems=${this.pinnedItems}
        .sidebarWidth=${this.sidebarWidth}
        .isNative=${this.isNative}
        @selectedType=${(eventArg) => {
          this.selectedType = eventArg.detail;
        }}
        @selectedItemName=${(eventArg) => {
          this.selectedItemName = eventArg.detail;
        }}
        @selectedItem=${(eventArg) => {
          this.selectedItem = eventArg.detail;
        }}
        @searchChanged=${(eventArg: CustomEvent) => {
          this.searchQuery = eventArg.detail;
          this.updateUrlWithScrollState();
        }}
        @pinnedChanged=${(eventArg: CustomEvent) => {
          this.pinnedItems = eventArg.detail;
          this.updateUrlWithScrollState();
        }}
        @widthChanged=${async (eventArg: CustomEvent) => {
          this.sidebarWidth = eventArg.detail;
          this.updateUrlWithScrollState();
          const frame = await this.wccFrame;
          if (frame) {
            frame.sidebarWidth = eventArg.detail;
            frame.requestUpdate();
          }
        }}
      ></wcc-sidebar>
      <wcc-properties
        .dashboardRef=${this}
        .warning="${this.warning}"
        .selectedItem=${this.selectedItem}
        .selectedViewport=${this.selectedViewport}
        .selectedTheme=${this.selectedTheme}
        .isNative=${this.isNative}
        .sidebarWidth=${this.sidebarWidth}
        @selectedViewport=${(eventArg) => {
          this.selectedViewport = eventArg.detail;
          this.scheduleUpdate();
        }}
        @selectedTheme=${(eventArg) => {
          this.selectedTheme = eventArg.detail;
        }}
        @editorStateChanged=${async (eventArg) => {
          const frame = await this.wccFrame;
          if (frame) {
            frame.advancedEditorOpen = eventArg.detail.isOpen;
            frame.requestUpdate();
          }
        }}
        @toggleNative=${() => {
          this.toggleNative();
        }}
      ></wcc-properties>
      <wcc-frame id="wccFrame" viewport=${this.selectedViewport} .isNative=${this.isNative} .sidebarWidth=${this.sidebarWidth}>
      </wcc-frame>
    `;
  }

  public setWarning(warningTextArg: string) {
    if (this.warning !== warningTextArg) {
      console.log(warningTextArg);
      this.warning = warningTextArg;
      setTimeout(() => {
        this.scheduleUpdate();
      }, 0);
    }
  }

  public toggleNative() {
    // Toggle between 'native' and 'desktop' viewports
    this.selectedViewport = this.selectedViewport === 'native' ? 'desktop' : 'native';
    this.buildUrl();
  }

  public async firstUpdated() {
    this.domtools = await plugins.deesDomtools.DomTools.setupDomTools();
    
    // Add ESC key handler for native mode
    document.addEventListener('keydown', (event) => {
      if (event.key === 'Escape' && this.isNative) {
        this.selectedViewport = 'desktop';
        this.buildUrl();
      }
    });
    
    // Set up scroll listeners after DOM is ready
    setTimeout(() => {
      this.setupScrollListeners();
    }, 500);
    
    // New route format with section name
    this.domtools.router.on(
      '/wcctools-route/:sectionName/:itemName/:demoIndex/:viewport/:theme',
      async (routeInfo) => {
        const sectionName = decodeURIComponent(routeInfo.params.sectionName);
        this.selectedSection = this.findSectionByName(sectionName);
        this.selectedItemName = routeInfo.params.itemName;
        this.selectedDemoIndex = parseInt(routeInfo.params.demoIndex) || 0;
        this.selectedViewport = routeInfo.params.viewport as breakpoints.TViewport;
        this.selectedTheme = routeInfo.params.theme as TTheme;

        if (this.selectedSection) {
          // Find item within the section
          const entries = getSectionItems(this.selectedSection);
          const found = entries.find(([name]) => name === routeInfo.params.itemName);
          if (found) {
            this.selectedItem = found[1];
            this.selectedType = this.selectedSection.type === 'elements' ? 'element' : 'page';
          }
        } else {
          // Fallback: try legacy format (element/page as section name)
          const legacyType = routeInfo.params.sectionName;
          if (legacyType === 'element' || legacyType === 'page') {
            this.selectedType = legacyType as TElementType;
            // Find item in any matching section
            const result = this.findItemByName(routeInfo.params.itemName);
            if (result) {
              this.selectedItem = result.item;
              this.selectedSection = result.section;
            }
          }
        }

        // Restore state from query parameters
        if (routeInfo.queryParams) {
          const search = routeInfo.queryParams.search;
          const frameScrollY = routeInfo.queryParams.frameScrollY;
          const sidebarScrollY = routeInfo.queryParams.sidebarScrollY;
          const pinned = routeInfo.queryParams.pinned;
          const sidebarWidth = routeInfo.queryParams.sidebarWidth;

          if (search) {
            this.searchQuery = search;
          } else {
            this.searchQuery = '';
          }
          if (frameScrollY) {
            this.frameScrollY = parseInt(frameScrollY);
          }
          if (sidebarScrollY) {
            this.sidebarScrollY = parseInt(sidebarScrollY);
          }
          if (pinned) {
            const newPinned = new Set(pinned.split(',').filter(Boolean));
            // Only update if actually different to avoid update loops
            if (this.pinnedItems.size !== newPinned.size ||
                ![...newPinned].every(k => this.pinnedItems.has(k))) {
              this.pinnedItems = newPinned;
            }
          } else if (this.pinnedItems.size > 0) {
            this.pinnedItems = new Set();
          }
          if (sidebarWidth) {
            this.sidebarWidth = parseInt(sidebarWidth, 10);
          }

          // Apply scroll positions and update frame after a short delay to ensure DOM is ready
          setTimeout(async () => {
            this.applyScrollPositions();
            // Ensure frame gets the sidebarWidth
            const frame = await this.wccFrame;
            if (frame) {
              frame.sidebarWidth = this.sidebarWidth;
              frame.requestUpdate();
            }
          }, 100);
        } else {
          this.searchQuery = '';
          // Only clear if not already empty to avoid update loops
          if (this.pinnedItems.size > 0) {
            this.pinnedItems = new Set();
          }
        }

        const domtoolsInstance = await plugins.deesDomtools.elementBasic.setup();
        this.selectedTheme === 'bright'
          ? domtoolsInstance.themeManager.goBright()
          : domtoolsInstance.themeManager.goDark();
      }
    );

    // Legacy route without demo index (for backwards compatibility)
    this.domtools.router.on(
      '/wcctools-route/:sectionName/:itemName/:viewport/:theme',
      async (routeInfo) => {
        const sectionName = decodeURIComponent(routeInfo.params.sectionName);
        this.selectedSection = this.findSectionByName(sectionName);
        this.selectedItemName = routeInfo.params.itemName;
        this.selectedDemoIndex = 0;
        this.selectedViewport = routeInfo.params.viewport as breakpoints.TViewport;
        this.selectedTheme = routeInfo.params.theme as TTheme;

        if (this.selectedSection) {
          const entries = getSectionItems(this.selectedSection);
          const found = entries.find(([name]) => name === routeInfo.params.itemName);
          if (found) {
            this.selectedItem = found[1];
            this.selectedType = this.selectedSection.type === 'elements' ? 'element' : 'page';
          }
        } else {
          // Fallback: try legacy format
          const legacyType = routeInfo.params.sectionName;
          if (legacyType === 'element' || legacyType === 'page') {
            this.selectedType = legacyType as TElementType;
            const result = this.findItemByName(routeInfo.params.itemName);
            if (result) {
              this.selectedItem = result.item;
              this.selectedSection = result.section;
            }
          }
        }

        // Restore state from query parameters
        if (routeInfo.queryParams) {
          const search = routeInfo.queryParams.search;
          const frameScrollY = routeInfo.queryParams.frameScrollY;
          const sidebarScrollY = routeInfo.queryParams.sidebarScrollY;
          const pinned = routeInfo.queryParams.pinned;
          const sidebarWidth = routeInfo.queryParams.sidebarWidth;

          if (search) {
            this.searchQuery = search;
          } else {
            this.searchQuery = '';
          }
          if (frameScrollY) {
            this.frameScrollY = parseInt(frameScrollY);
          }
          if (sidebarScrollY) {
            this.sidebarScrollY = parseInt(sidebarScrollY);
          }
          if (pinned) {
            const newPinned = new Set(pinned.split(',').filter(Boolean));
            // Only update if actually different to avoid update loops
            if (this.pinnedItems.size !== newPinned.size ||
                ![...newPinned].every(k => this.pinnedItems.has(k))) {
              this.pinnedItems = newPinned;
            }
          } else if (this.pinnedItems.size > 0) {
            this.pinnedItems = new Set();
          }
          if (sidebarWidth) {
            this.sidebarWidth = parseInt(sidebarWidth, 10);
          }

          // Apply scroll positions after a short delay to ensure DOM is ready
          setTimeout(() => {
            this.applyScrollPositions();
          }, 100);
        } else {
          this.searchQuery = '';
          // Only clear if not already empty to avoid update loops
          if (this.pinnedItems.size > 0) {
            this.pinnedItems = new Set();
          }
        }

        const domtoolsInstance = await plugins.deesDomtools.elementBasic.setup();
        this.selectedTheme === 'bright'
          ? domtoolsInstance.themeManager.goBright()
          : domtoolsInstance.themeManager.goDark();
      }
    );
  }

  public async updated(changedPropertiesArg: Map<string, any>) {
    this.domtools = await plugins.deesDomtools.DomTools.setupDomTools();
    await this.domtools.router._handleRouteState();
    const wccFrame: WccFrame = this.shadowRoot.querySelector('wcc-frame');

    if (changedPropertiesArg.has('selectedItemName')) {
      document.title = this.selectedItemName;
    };

    if (this.selectedType === 'page' && this.selectedItem) {
      if (typeof this.selectedItem === 'function') {
        console.log('slotting page.');
        const viewport = await wccFrame.getViewportElement();
        const pageFactory = this.selectedItem as TTemplateFactory;
        const pageTemplate = await resolveTemplateFactory(pageFactory);
        render(pageTemplate, viewport);
        console.log('rendered page.');
      } else {
        console.error('The selected item looks strange:');
        console.log(this.selectedItem);
      }
    } else if (this.selectedType === 'element' && this.selectedItem) {
      console.log('slotting element.');
      const anonItem: any = this.selectedItem;
      if (!anonItem.demo) {
        this.setWarning(`component ${anonItem.name} does not expose a demo property.`);
        return;
      }

      // Support both single demo (function) and multiple demos (array)
      const isArray = Array.isArray(anonItem.demo);
      const isFunction = typeof anonItem.demo === 'function';

      if (!isArray && !isFunction) {
        this.setWarning(
          `component ${anonItem.name} has demo property, but it is not a function or array of functions`
        );
        return;
      }

      // Get the specific demo to render
      const demoFactory = getDemoAtIndex(anonItem.demo, this.selectedDemoIndex);
      if (!demoFactory) {
        this.setWarning(
          `component ${anonItem.name} does not have a demo at index ${this.selectedDemoIndex + 1}`
        );
        return;
      }

      this.setWarning(null);
      const viewport = await wccFrame.getViewportElement();
      const demoTemplate = await resolveTemplateFactory(demoFactory);
      render(demoTemplate, viewport);
    }
  }

  public buildUrl() {
    const sectionName = this.selectedSection
      ? encodeURIComponent(this.selectedSection.name)
      : this.selectedType; // Fallback for legacy
    const baseUrl = `/wcctools-route/${sectionName}/${this.selectedItemName}/${this.selectedDemoIndex}/${this.selectedViewport}/${this.selectedTheme}`;
    const queryParams = new URLSearchParams();

    if (this.searchQuery) {
      queryParams.set('search', this.searchQuery);
    }
    if (this.frameScrollY > 0) {
      queryParams.set('frameScrollY', this.frameScrollY.toString());
    }
    if (this.sidebarScrollY > 0) {
      queryParams.set('sidebarScrollY', this.sidebarScrollY.toString());
    }
    if (this.pinnedItems.size > 0) {
      queryParams.set('pinned', Array.from(this.pinnedItems).join(','));
    }
    if (this.sidebarWidth !== 200) {
      queryParams.set('sidebarWidth', this.sidebarWidth.toString());
    }

    const queryString = queryParams.toString();
    const fullUrl = queryString ? `${baseUrl}?${queryString}` : baseUrl;

    this.domtools.router.pushUrl(fullUrl);
  }
  
  private scrollUpdateTimeout: NodeJS.Timeout;
  private scrollListenersAttached: boolean = false;
  
  public async setupScrollListeners() {
    // Prevent duplicate listeners
    if (this.scrollListenersAttached) {
      return;
    }

    const wccFrame = await this.wccFrame;
    const wccSidebar = this.shadowRoot.querySelector('wcc-sidebar') as WccSidebar | null;

    if (wccFrame) {
      // The frame element itself is the scrollable container
      wccFrame.addEventListener('scroll', () => {
        this.frameScrollY = wccFrame.scrollTop;
        this.debouncedScrollUpdate();
      });
      this.scrollListenersAttached = true;
    }
    
    if (wccSidebar) {
      // Use the sidebar's scrollable container (.menu element)
      const scrollContainer = wccSidebar.scrollableContainer;
      if (scrollContainer) {
        scrollContainer.addEventListener('scroll', () => {
          this.sidebarScrollY = scrollContainer.scrollTop;
          this.debouncedScrollUpdate();
        });
      }
    }
  }
  
  private debouncedScrollUpdate() {
    clearTimeout(this.scrollUpdateTimeout);
    this.scrollUpdateTimeout = setTimeout(() => {
      this.updateUrlWithScrollState();
    }, 300);
  }
  
  private updateUrlWithScrollState() {
    const sectionName = this.selectedSection
      ? encodeURIComponent(this.selectedSection.name)
      : this.selectedType; // Fallback for legacy
    const baseUrl = `/wcctools-route/${sectionName}/${this.selectedItemName}/${this.selectedDemoIndex}/${this.selectedViewport}/${this.selectedTheme}`;
    const queryParams = new URLSearchParams();

    if (this.searchQuery) {
      queryParams.set('search', this.searchQuery);
    }
    if (this.frameScrollY > 0) {
      queryParams.set('frameScrollY', this.frameScrollY.toString());
    }
    if (this.sidebarScrollY > 0) {
      queryParams.set('sidebarScrollY', this.sidebarScrollY.toString());
    }
    if (this.pinnedItems.size > 0) {
      queryParams.set('pinned', Array.from(this.pinnedItems).join(','));
    }
    if (this.sidebarWidth !== 200) {
      queryParams.set('sidebarWidth', this.sidebarWidth.toString());
    }

    const queryString = queryParams.toString();
    const fullUrl = queryString ? `${baseUrl}?${queryString}` : baseUrl;

    // Use replaceState to update URL without navigation
    window.history.replaceState(null, '', fullUrl);
  }
  
  public async applyScrollPositions() {
    // Only apply scroll positions once to avoid interfering with user scrolling
    if (this.scrollPositionsApplied) {
      return;
    }

    const wccFrame = await this.wccFrame;
    const wccSidebar = this.shadowRoot.querySelector('wcc-sidebar') as WccSidebar | null;

    if (wccFrame && this.frameScrollY > 0) {
      // The frame element itself is the scrollable container
      wccFrame.scrollTop = this.frameScrollY;
    }
    
    if (wccSidebar && this.sidebarScrollY > 0) {
      // Use the sidebar's scrollable container (.menu element)
      const scrollContainer = wccSidebar.scrollableContainer;
      if (scrollContainer) {
        scrollContainer.scrollTop = this.sidebarScrollY;
      }
    }
    
    this.scrollPositionsApplied = true;
  }
}
