import {
  DeesElement,
  html,
  property,
  customElement,
  cssManager,
  css,
  type CSSResult,
} from '@design.estate/dees-element';

import * as domtools from '@design.estate/dees-domtools';
import { themeDefaultStyles } from '../00theme.js';

import { icon, type IconDefinition } from '@fortawesome/fontawesome-svg-core';
import {
  faFacebook,
  faGoogle,
  faLinkedin,
  faMedium,
  faSlackHash,
  faTwitter,
  faInstagram,
  faTiktok,
} from '@fortawesome/free-brands-svg-icons';

import {
  faCopy as faCopyRegular,
  faCircleCheck as faCircleCheckRegular,
  faCircleXmark as faCircleXmarkRegular,
  faMessage as faMessageRegular,
  faPaste as faPasteRegular,
  faSun as faSunRegular,
  faTrashCan as faTrashCanRegular,
} from '@fortawesome/free-regular-svg-icons';
import {
  faArrowRight as faArrowRightSolid,
  faArrowUpRightFromSquare as faArrowUpRightFromSquareSolid,
  faBell as faBellSolid,
  faBug as faBugSolid,
  faBuilding as faBuildingSolid,
  faCaretLeft as faCaretLeftSolid,
  faCaretRight as faCaretRightSolid,
  faCheck as faCheckSolid,
  faCircleInfo as faCircleInfoSolid,
  faCircleCheck as faCircleCheckSolid,
  faCircleXmark as faCircleXmarkSolid,
  faClockRotateLeft as faClockRotateLeftSolid,
  faCopy as faCopySolid,
  faDesktop as faDesktopSolid,
  faEye as faEyeSolid,
  faEyeSlash as faEyeSlashSolid,
  faFileInvoice as faFileInvoiceSolid,
  faFileInvoiceDollar as faFileInvoiceDollarSolid,
  faGear as faGearSolid,
  faGrip as faGripSolid,
  faMagnifyingGlass as faMagnifyingGlassSolid,
  faMessage as faMessageSolid,
  faMoneyCheckDollar as faMoneyCheckDollarSolid,
  faMugHot as faMugHotSolid,
  faMinus as faMinusSolid,
  faNetworkWired as faNetworkWiredSolid,
  faPaperclip as faPaperclipSolid,
  faPaste as faPasteSolid,
  faPenToSquare as faPenToSquareSolid,
  faPlus as faPlusSolid,
  faReceipt as faReceiptSolid,
  faRss as faRssSolid,
  faUsers as faUsersSolid,
  faShare as faShareSolid,
  faSun as faSunSolid,
  faTerminal as faTerminalSolid,
  faTrash as faTrashSolid,
  faTrashCan as faTrashCanSolid,
  faWallet as faWalletSolid,
  faXmark as faXmarkSolid,
} from '@fortawesome/free-solid-svg-icons';
import { demoFunc } from './dees-icon.demo.js';

// Import Lucide icons and the createElement function
import * as lucideIcons from 'lucide';
import { createElement } from 'lucide';

// Collect FontAwesome icons
const faIcons = {
  // normal
  arrowRight: faArrowRightSolid,
  arrowUpRightFromSquare: faArrowUpRightFromSquareSolid,
  bell: faBellSolid,
  bug: faBugSolid,
  building: faBuildingSolid,
  caretLeft: faCaretLeftSolid,
  caretRight: faCaretRightSolid,
  check: faCheckSolid,
  circleInfo: faCircleInfoSolid,
  circleCheck: faCircleCheckRegular,
  circleCheckSolid: faCircleCheckSolid,
  circleXmark: faCircleXmarkRegular,
  circleXmarkSolid: faCircleXmarkSolid,
  clockRotateLeft: faClockRotateLeftSolid,
  copy: faCopyRegular,
  copySolid: faCopySolid,
  desktop: faDesktopSolid,
  eye: faEyeSolid,
  eyeSlash: faEyeSlashSolid,
  fileInvoice: faFileInvoiceSolid,
  fileInvoiceDoller: faFileInvoiceDollarSolid,
  gear: faGearSolid,
  grip: faGripSolid,
  magnifyingGlass: faMagnifyingGlassSolid,
  message: faMessageRegular,
  messageSolid: faMessageSolid,
  moneyCheckDollar: faMoneyCheckDollarSolid,
  mugHot: faMugHotSolid,
  minus: faMinusSolid,
  networkWired: faNetworkWiredSolid,
  paperclip: faPaperclipSolid,
  paste: faPasteRegular,
  pasteSolid: faPasteSolid,
  penToSquare: faPenToSquareSolid,
  plus: faPlusSolid,
  receipt: faReceiptSolid,
  rss: faRssSolid,
  share: faShareSolid,
  sun: faSunRegular,
  sunSolid: faSunSolid,
  terminal: faTerminalSolid,
  trash: faTrashSolid,
  trashSolid: faTrashSolid,
  trashCan: faTrashCanRegular,
  trashCanSolid: faTrashCanSolid,
  users: faUsersSolid,
  wallet: faWalletSolid,
  xmark: faXmarkSolid,
  // brands
  facebook: faFacebook,
  google: faGoogle,
  instagram: faInstagram,
  linkedin: faLinkedin,
  medium: faMedium,
  slack: faSlackHash,
  tiktok: faTiktok,
  twitter: faTwitter,
};

// Create a string literal type for all FA icons
type FAIconKey = keyof typeof faIcons;

// Create union types for the icons with prefixes
export type IconWithPrefix = `fa:${FAIconKey}` | `lucide:${string}`;

// Export only FontAwesome icons directly
export const icons = {
  fa: faIcons
};

// Legacy type for backward compatibility
export type TIconKey = FAIconKey | `lucide:${string}`;

// Use a global static cache for all icons to reduce rendering
const iconCache = new Map<string, string>();

// Clear cache items occasionally to prevent memory leaks 
const MAX_CACHE_SIZE = 500;
function limitCacheSize() {
  if (iconCache.size > MAX_CACHE_SIZE) {
    // Remove oldest entries (first 20% of items)
    const keysToDelete = Array.from(iconCache.keys()).slice(0, MAX_CACHE_SIZE / 5);
    keysToDelete.forEach(key => iconCache.delete(key));
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'dees-icon': DeesIcon;
  }
}

@customElement('dees-icon')
export class DeesIcon extends DeesElement {
  public static demo = demoFunc;

  /**
   * @deprecated Use the `icon` property instead with format "fa:iconName" or "lucide:iconName"
   */
  @property({
    type: String,
    converter: {
      // Convert attribute string to property (for reflected attributes)
      fromAttribute: (value: string): TIconKey => value as TIconKey,
      // Convert property to attribute (for reflection)
      toAttribute: (value: TIconKey): string => value
    }
  })
  accessor iconFA: TIconKey | undefined = undefined;

  /**
   * The preferred icon property. Use format "fa:iconName" or "lucide:iconName"
   * Examples: "fa:check", "lucide:menu"
   */
  @property({
    type: String,
    converter: {
      fromAttribute: (value: string): IconWithPrefix => value as IconWithPrefix,
      toAttribute: (value: IconWithPrefix): string => value
    }
  })
  accessor icon: IconWithPrefix | undefined = undefined;

  @property({ type: Number })
  accessor iconSize: number;

  @property({ type: String })
  accessor color: string = 'currentColor';

  @property({ type: Number })
  accessor strokeWidth: number = 2;

  // For tracking when we need to re-render
  private lastIcon: IconWithPrefix | TIconKey | null = null;
  private lastIconSize: number | null = null;
  private lastColor: string | null = null;
  private lastStrokeWidth: number | null = null;

  constructor() {
    super();
    domtools.elementBasic.setup();
  }

  /**
   * Gets the effective icon value, supporting both the new `icon` property
   * and the legacy `iconFA` property for backward compatibility.
   * Prefers `icon` if both are set.
   */
  private getEffectiveIcon(): IconWithPrefix | TIconKey | null {
    // Prefer the new API
    if (this.icon) {
      return this.icon;
    }
    
    // Fall back to the old API
    if (this.iconFA) {
      // If iconFA is already in the proper format (lucide:name), use it directly
      if (this.iconFA.startsWith('lucide:')) {
        return this.iconFA;
      }
      
      // For FontAwesome icons with no prefix, add the prefix
      return `fa:${this.iconFA}` as IconWithPrefix;
    }
    
    return null;
  }
  
  /**
   * Parses an icon string into its type and name parts
   * @param iconStr The icon string in format "type:name"
   * @returns Object with type and name properties
   */
  private parseIconString(iconStr: string): { type: 'fa' | 'lucide', name: string } {
    const lowerStr = iconStr.toLowerCase();
    if (lowerStr.startsWith('fa:')) {
      return {
        type: 'fa',
        name: iconStr.substring(3) // Remove 'fa:' prefix
      };
    } else if (lowerStr.startsWith('lucide:')) {
      return {
        type: 'lucide',
        name: iconStr.substring(7) // Remove 'lucide:' prefix
      };
    } else {
      // Default to Lucide when no prefix is provided
      return {
        type: 'lucide',
        name: iconStr
      };
    }
  }

  private renderLucideIcon(iconName: string): string {
    // Create a cache key based on all visual properties
    const cacheKey = `lucide:${iconName}:${this.iconSize}:${this.color}:${this.strokeWidth}`;
    
    // Check if we already have this icon in the cache
    if (iconCache.has(cacheKey)) {
      return iconCache.get(cacheKey) || '';
    }

    try {
      // Get the Pascal case icon name (Menu instead of menu)
      const pascalCaseName = iconName.charAt(0).toUpperCase() + iconName.slice(1);
      
      // Check if the icon exists in lucideIcons
      if (!lucideIcons[pascalCaseName]) {
        console.warn(`Lucide icon '${pascalCaseName}' not found in lucideIcons object`);
        return '';
      }
      
      // Use the exact pattern from Lucide documentation
      const svgElement = createElement(lucideIcons[pascalCaseName], {
        color: this.color,
        size: this.iconSize,
        strokeWidth: this.strokeWidth
      });
      
      if (!svgElement) {
        console.warn(`createElement returned empty result for ${pascalCaseName}`);
        return '';
      }
      
      // Get the HTML
      const result = svgElement.outerHTML;
      
      // Cache the result for future use
      iconCache.set(cacheKey, result);
      limitCacheSize();
      
      return result;
    } catch (error) {
      console.error(`Error rendering Lucide icon ${iconName}:`, error);
      
      // Create a fallback SVG with the icon name
      return `<svg xmlns="http://www.w3.org/2000/svg" width="${this.iconSize}" height="${this.iconSize}" viewBox="0 0 24 24" fill="none" stroke="${this.color}" stroke-width="${this.strokeWidth}" stroke-linecap="round" stroke-linejoin="round">
        <text x="50%" y="50%" font-size="6" text-anchor="middle" dominant-baseline="middle" fill="${this.color}">${iconName}</text>
      </svg>`;
    }
  }

  public static styles = [
    themeDefaultStyles,
    cssManager.defaultStyles,
    css`
      /* TODO: Migrate hardcoded values to --dees-* CSS variables */
      :host {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        line-height: 1;
        vertical-align: middle;
      }

      /* Improve rendering performance */
      #iconContainer svg {
        display: block;
        height: 100%;
        width: 100%;
        will-change: transform; /* Helps with animations */
        contain: strict; /* Performance optimization */
      }
    `,
  ];

  public render() {
    return html`
      ${domtools.elementBasic.styles}
      <style>
        #iconContainer {
          width: ${this.iconSize}px;
          height: ${this.iconSize}px;
        }
      </style>
      <div id="iconContainer"></div>
    `;
  }

  public updated() {
    // If size is not specified, use font size as a base
    if (!this.iconSize) {
      this.iconSize = parseInt(globalThis.getComputedStyle(this).fontSize.replace(/\D/g,''));
    }
    
    // Get the effective icon (either from icon or iconFA property)
    const effectiveIcon = this.getEffectiveIcon();
    
    // Check if we actually need to update the icon
    // This prevents unnecessary DOM operations when properties haven't changed
    if (this.lastIcon === effectiveIcon && 
        this.lastIconSize === this.iconSize && 
        this.lastColor === this.color && 
        this.lastStrokeWidth === this.strokeWidth) {
      return; // No visual changes - skip update
    }
    
    // Update our "last properties" for future change detection
    this.lastIcon = effectiveIcon;
    this.lastIconSize = this.iconSize;
    this.lastColor = this.color;
    this.lastStrokeWidth = this.strokeWidth;
    
    const container = this.shadowRoot?.querySelector('#iconContainer');
    if (!container || !effectiveIcon) return;

    try {
      // Parse the icon string to get type and name
      const { type, name } = this.parseIconString(effectiveIcon);
      
      if (type === 'lucide') {
        // For Lucide, use direct DOM manipulation as shown in the docs
        // This approach avoids HTML string issues
        container.innerHTML = ''; // Clear container
        
        try {
          // Convert to PascalCase
          const pascalCaseName = name.charAt(0).toUpperCase() + name.slice(1);
          
          if (lucideIcons[pascalCaseName]) {
            // Use the documented pattern from Lucide docs
            const svgElement = createElement(lucideIcons[pascalCaseName], {
              color: this.color,
              size: this.iconSize,
              strokeWidth: this.strokeWidth
            });
            
            if (svgElement) {
              // Directly append the element
              container.appendChild(svgElement);
              return; // Exit early since we've added the element
            }
          }
          
          // If we reach here, something went wrong
          throw new Error(`Could not create element for ${pascalCaseName}`);
        } catch (error) {
          console.error(`Error rendering Lucide icon:`, error);
          
          // Fall back to the string-based approach
          const iconHtml = this.renderLucideIcon(name);
          if (iconHtml) {
            container.innerHTML = iconHtml;
          }
        }
      } else {
        // Use FontAwesome rendering via HTML string
        const faIcon = icons.fa[name as FAIconKey];
        if (faIcon) {
          const iconHtml = icon(faIcon).html[0];
          container.innerHTML = iconHtml;
        } else {
          console.warn(`FontAwesome icon not found: ${name}`);
        }
      }
    } catch (error) {
      console.error(`Error updating icon ${effectiveIcon}:`, error);
    }
  }

  // Clean up resources when element is removed
  async disconnectedCallback() {
    super.disconnectedCallback();
    
    // Clear our references
    this.lastIcon = null;
    this.lastIconSize = null;
    this.lastColor = null;
    this.lastStrokeWidth = null;
  }
}