import type { Tile, TileGrid, TileRow, TileColumn, TilePanel, TileTab, TileContent, TileType } from './types/tile-types';
import { generateId } from './utils/tile-utils';
import type { Component } from 'svelte';
import type { TtabsTheme } from './types/theme-types';
import { DEFAULT_THEME, resolveTheme } from './types/theme-types';

/**
 * Type for component registry
 */
interface ContentComponent {
  component: Component<any>;
  defaultProps?: Record<string, any>;
}

/**
 * Type for state change callback
 */
export type StateChangeCallback = (state: Record<string, Tile>) => void;

/**
 * Options for creating a ttabs instance
 */
export interface TtabsOptions {
  /**
   * Initial tiles state (optional)
   * If provided, the instance will be initialized with these tiles
   * If not provided, a default root grid will be created
   */
  tiles?: Record<string, Tile> | Tile[];

  /**
   * Initially focused tab (optional)
   * If provided, this tab will be set as the focused active tab
   */
  focusedTab?: string;

  /**
   * Theme configuration (optional)
   * If not provided, the default theme will be used
   */
  theme?: TtabsTheme;
}

/**
 * Ttabs class implementation
 */
export class Ttabs {

  tiles = $state<Record<string, Tile>>({});
  activePanel = $state<string | null>(null);
  focusedActiveTab = $state<string | null>(null);
  rootGridId = $state<string>('');
  componentRegistry = $state<Record<string, ContentComponent>>({});
  theme = $state<TtabsTheme>(DEFAULT_THEME);

  // State change listeners
  stateChangeListeners: StateChangeCallback[] = [];

  /**
   * Find the root grid ID from the current tiles
   * @returns The ID of the root grid
   * @throws Error if no root grid is found
   * @private
   */
  private findRootGridId(): string {
    const rootGridId = Object.values(this.tiles)
      .find(tile => tile.type === 'grid' && !tile.parent)?.id || '';

    if (rootGridId === '') {
      throw new Error('No root grid found');
    }

    return rootGridId;
  }

  constructor(options: TtabsOptions = {}) {
    // Initialize state
    if (options.tiles) {
      if (Array.isArray(options.tiles)) {
        // Convert array to record
        this.tiles = {};

        // Add each tile to the state
        options.tiles.forEach(tile => {
          this.tiles[tile.id] = tile;
        });

        // Find the root grid in the initial state
        this.rootGridId = this.findRootGridId();
      } else {
        // Record format provided directly
        this.tiles = options.tiles;

        // Find the root grid
        this.rootGridId = this.findRootGridId();
      }
    } else {
      // Auto-create a root grid if no initial state is provided
      this.rootGridId = this.addGrid();
    }

    // Initialize theme with resolution for inheritance
    if (options.theme) {
      this.theme = resolveTheme(options.theme);
    } else {
      this.theme = DEFAULT_THEME;
    }

    // Set focused tab if provided
    if (options.focusedTab && this.getTile<TileTab>(options.focusedTab)) {
      this.focusedActiveTab = options.focusedTab;
    }
  }

  /**
   * Subscribe to state changes
   * @param callback Function to call when state changes
   * @returns Unsubscribe function
   */
  subscribe(callback: StateChangeCallback): () => void {
    this.stateChangeListeners.push(callback);

    // Call immediately with current state
    callback(this.tiles);

    // Return unsubscribe function
    return () => {
      this.stateChangeListeners = this.stateChangeListeners.filter(cb => cb !== callback);
    };
  }

  /**
   * Notify all subscribers of state change
   */
  private notifyStateChange(): void {
    this.stateChangeListeners.forEach(callback => {
      callback(this.tiles);
    });
  }

  /**
   * Register a component for content rendering
   * @param componentId Unique identifier for the component
   * @param component Svelte component to render
   * @param defaultProps Optional default props for the component
   */
  registerComponent(
    componentId: string,
    component: Component<any>,
    defaultProps: Record<string, any> = {}
  ): void {
    this.componentRegistry[componentId] = { component, defaultProps };
  }

  /**
   * Get a registered component by ID
   * @param componentId The component identifier
   * @returns The component and its default props, or null if not found
   */
  getContentComponent(componentId: string): ContentComponent | null {
    return this.componentRegistry[componentId] || null;
  }

  /**
   * Check if a component is registered
   * @param componentId The component identifier
   * @returns True if the component is registered
   */
  hasContentComponent(componentId: string): boolean {
    return !!this.componentRegistry[componentId];
  }

  /**
   * Set component to a column or a tab
   * @param parentId ID of the parent column or tab
   * @param componentId ID of the registered component
   * @param props Props to pass to the component
   * @returns ID of the new content with component
   */
  setComponent(
    parentId: string,
    componentId: string,
    props: Record<string, any> = {}
  ): string {
    // Verify component exists
    if (!this.hasContentComponent(componentId)) {
      throw new Error(`Component with ID ${componentId} is not registered`);
    }

    // Get parent tile
    const parent = this.getTile(parentId);
    if (!parent) {
      throw new Error(`Parent tile with ID ${parentId} not found`);
    }

    // Handle different parent types
    if (parent.type === 'tab') {
      // Tab content handling
      const tab = parent as TileTab;

      // Check if tab already has content - update it instead of creating new
      if (tab.content && this.getTile(tab.content)) {
        const existingContent = this.getTile<TileContent>(tab.content);
        if (existingContent && existingContent.type === 'content') {
          this.updateTile(tab.content, {
            componentId,
            data: {
              ...existingContent.data,
              componentProps: props
            }
          });
          return tab.content;
        }
      }

      // Create the content
      const contentId = this.addTile({
        type: 'content',
        parent: parentId,
        componentId,
        data: {
          componentProps: props
        }
      });

      // Update tab's content
      this.updateTile(parentId, {
        content: contentId
      });

      return contentId;
    }
    else if (parent.type === 'column') {
      // Column content handling
      const column = parent as TileColumn;

      // Check if column already has a child
      if (column.child && this.getTile(column.child)) {
        // Remove existing child if it's content type
        const existingChild = this.getTile(column.child);
        if (existingChild && existingChild.type === 'content') {
          // Update existing content instead of creating new
          this.updateTile(column.child, {
            componentId,
            data: {
              componentProps: props
            }
          });
          return column.child;
        } else {
          // Remove existing child that's not content type
          this.removeTile(column.child);
        }
      }

      // Create the content
      const contentId = this.addTile({
        type: 'content',
        parent: parentId,
        componentId,
        data: {
          componentProps: props
        }
      });

      // Update column's child reference
      this.updateTile(parentId, {
        child: contentId
      });

      return contentId;
    }
    else {
      throw new Error(`Cannot add content to a parent of type ${parent.type}. Content can only be child of a tab or column.`);
    }
  }

  /**
   * Get all tiles
   */
  getTiles(): Record<string, Tile> {
    return this.tiles;
  }

  /**
   * Get the active panel ID
   */
  getActivePanel(): string | null {
    return this.activePanel;
  }

  /**
   * Get a specific tile by ID with type casting
   */
  getTile<T extends Tile = Tile>(id: string): T | null {
    return (this.tiles[id] || null) as T | null;
  }

  /**
   * Get a grid by ID, throwing an error if not found
   * @throws Error if the tile is not found or not a grid
   */
  getGrid(id: string): TileGrid {
    const tile = this.tiles[id];
    if (!tile) {
      throw new Error(`Grid not found: ${id}`);
    }
    if (tile.type !== 'grid') {
      throw new Error(`Tile ${id} is not a grid, found ${tile.type}`);
    }
    return tile as TileGrid;
  }

  /**
   * Get a row by ID, throwing an error if not found
   * @throws Error if the tile is not found or not a row
   */
  getRow(id: string): TileRow {
    const tile = this.tiles[id];
    if (!tile) {
      throw new Error(`Row not found: ${id}`);
    }
    if (tile.type !== 'row') {
      throw new Error(`Tile ${id} is not a row, found ${tile.type}`);
    }
    return tile as TileRow;
  }

  /**
   * Get a column by ID, throwing an error if not found
   * @throws Error if the tile is not found or not a column
   */
  getColumn(id: string): TileColumn {
    const tile = this.tiles[id];
    if (!tile) {
      throw new Error(`Column not found: ${id}`);
    }
    if (tile.type !== 'column') {
      throw new Error(`Tile ${id} is not a column, found ${tile.type}`);
    }
    return tile as TileColumn;
  }

  /**
   * Get a panel by ID, throwing an error if not found
   * @throws Error if the tile is not found or not a panel
   */
  getPanel(id: string): TilePanel {
    const tile = this.tiles[id];
    if (!tile) {
      throw new Error(`Panel not found: ${id}`);
    }
    if (tile.type !== 'panel') {
      throw new Error(`Tile ${id} is not a panel, found ${tile.type}`);
    }
    return tile as TilePanel;
  }

  /**
   * Get a tab by ID, throwing an error if not found
   * @throws Error if the tile is not found or not a tab
   */
  getTab(id: string): TileTab {
    const tile = this.tiles[id];
    if (!tile) {
      throw new Error(`Tab not found: ${id}`);
    }
    if (tile.type !== 'tab') {
      throw new Error(`Tile ${id} is not a tab, found ${tile.type}`);
    }
    return tile as TileTab;
  }

  /**
   * Get children of a tile filtered by type
   */
  getChildren(parentId: string, tileType: TileType | null = null): Tile[] {
    return Object.values(this.tiles)
      .filter((tile): tile is Tile => Boolean(tile))
      .filter(tile => tile.parent === parentId)
      .filter(tile => !tileType || tile.type === tileType);
  }

  /**
   * Get the currently active panel
   */
  getActivePanelTile(): TilePanel | null {
    return this.activePanel ? this.getTile<TilePanel>(this.activePanel) : null;
  }

  /**
   * Get the active tab of the active panel
   */
  getActivePanelTab(): TileTab | null {
    const panel = this.getActivePanelTile();
    if (!panel || !panel.activeTab) return null;
    return this.getTile<TileTab>(panel.activeTab);
  }

  /**
   * Get the content associated with a tab
   */
  getTabContent(tabId: string): TileContent | null {
    const tab = this.getTile<TileTab>(tabId);
    if (!tab) return null;
    return this.getTile<TileContent>(tab.content);
  }

  /**
   * Add a new tile to the layout
   * @param tile Tile to add (requires type property)
   * @returns ID of the new tile
   */
  addTile<T extends Tile>(tile: Partial<T> & { type: T['type'] }): string {
    // Generate ID if not provided
    const id = tile.id || generateId();

    // Create a new tile with the ID
    const newTile = {
      id,
      ...tile
    } as T;

    // Add to state
    this.tiles[id] = newTile;

    // Notify state changes
    this.notifyStateChange();

    return id;
  }

  /**
   * Update a tile with the given changes
   * @param id ID of the tile to update
   * @param updates Changes to apply
   * @returns True if successful
   */
  updateTile<T extends Tile>(id: string, updates: Partial<T>): boolean {
    const tile = this.getTile(id);
    if (!tile) return false;

    this.tiles[id] = { ...tile, ...updates };

    // Notify state changes
    this.notifyStateChange();

    return true;
  }

  /**
   * Find all tiles that reference the specified tile
   * @param tileId ID of the tile to find references to
   * @returns Array of tiles that reference the specified tile
   */
  private findTilesReferencingTile(tileId: string): Tile[] {
    return Object.values(this.tiles).filter(tile => {
      if (tile.type === 'grid' && tile.rows.includes(tileId)) {
        return true;
      }
      if (tile.type === 'row' && tile.columns.includes(tileId)) {
        return true;
      }
      if (tile.type === 'column' && tile.child === tileId) {
        return true;
      }
      if (tile.type === 'panel' && tile.tabs.includes(tileId)) {
        return true;
      }
      if (tile.type === 'tab' && tile.content === tileId) {
        return true;
      }
      return false;
    });
  }

  /**
   * Remove a tile from the layout
   * @param id ID of the tile to remove
   * @returns True if successful
   */
  removeTile(id: string): boolean {
    const tile = this.getTile(id);
    if (!tile) return false;

    // Check for references to this tile throughout the layout
    const referencingTiles = this.findTilesReferencingTile(id);

    // Remove references to this tile
    referencingTiles.forEach((referencingTile: Tile) => {
      if (referencingTile.type === 'grid' && 'rows' in referencingTile) {
        this.updateTile(referencingTile.id, {
          rows: referencingTile.rows.filter((rowId: string) => rowId !== id)
        });
      }
      else if (referencingTile.type === 'row' && 'columns' in referencingTile) {
        this.updateTile(referencingTile.id, {
          columns: referencingTile.columns.filter((colId: string) => colId !== id)
        });
      }
      else if (referencingTile.type === 'column' && 'child' in referencingTile) {
        if (referencingTile.child === id) {
          this.updateTile(referencingTile.id, {
            child: ''
          });
        }
      }
      else if (referencingTile.type === 'panel' && 'tabs' in referencingTile) {
        this.updateTile(referencingTile.id, {
          tabs: referencingTile.tabs.filter((tabId: string) => tabId !== id),
          activeTab: referencingTile.activeTab === id ? null : referencingTile.activeTab
        });
      }
      else if (referencingTile.type === 'tab' && 'content' in referencingTile) {
        if (referencingTile.content === id) {
          this.updateTile(referencingTile.id, {
            content: ''
          });
        }
      }
    });

    // Delete the tile itself
    delete this.tiles[id];

    // Notify state changes
    this.notifyStateChange();

    return true;
  }

  /**
   * Set the active panel
   */
  setActivePanel(id: string): boolean {
    const panel = this.getTile<TilePanel>(id);
    if (!panel || panel.type !== 'panel') return false;

    // With runes we can directly assign
    this.activePanel = id;
    return true;
  }

  /**
   * Set the active tab
   */
  setActiveTab(tabId: string): boolean {
    const tab = this.getTile<TileTab>(tabId);
    if (!tab || tab.type !== 'tab') return false;

    const panelId = tab.parent;
    if (!panelId) return false;

    const panel = this.getTile<TilePanel>(panelId);
    if (!panel || panel.type !== 'panel') return false;

    this.updateTile(panelId, { activeTab: tabId });
    this.setActivePanel(panelId);

    // Also set as focused tab
    this.focusedActiveTab = tabId;

    return true;
  }

  /**
   * Set the focused active tab
   * @param tabId ID of the tab to focus
   * @returns True if successful
   */
  setFocusedActiveTab(tabId: string): boolean {
    const tab = this.getTile<TileTab>(tabId);
    if (!tab || tab.type !== 'tab') return false;

    // Ensure the tab is active in its panel
    const panelId = tab.parent;
    if (!panelId) return false;

    const panel = this.getTile<TilePanel>(panelId);
    if (!panel || panel.type !== 'panel') return false;

    // If the tab is not the active one in its panel, make it active
    if (panel.activeTab !== tabId) {
      this.updateTile(panelId, { activeTab: tabId });
    }

    // Set the panel as active if it's not already
    if (this.activePanel !== panelId) {
      this.setActivePanel(panelId);
    }

    // Set the focused tab
    this.focusedActiveTab = tabId;

    return true;
  }

  /**
   * Get the focused active tab
   */
  getFocusedActiveTabTile(): TileTab | null {
    return this.focusedActiveTab ? this.getTile<TileTab>(this.focusedActiveTab) : null;
  }

  /**
   * Reorder tabs within a panel
   */
  reorderTabs(panelId: string, oldIndex: number, newIndex: number): boolean {
    const panel = this.getTile<TilePanel>(panelId);
    if (!panel || panel.type !== 'panel') return false;

    // Make sure indices are valid
    if (oldIndex < 0 || oldIndex >= panel.tabs.length ||
      newIndex < 0 || newIndex >= panel.tabs.length) {
      return false;
    }

    // Create a new array with reordered tabs
    const reorderedTabs = [...panel.tabs];
    const [movedTab] = reorderedTabs.splice(oldIndex, 1);
    reorderedTabs.splice(newIndex, 0, movedTab);

    // Update the panel
    this.updateTile(panelId, { tabs: reorderedTabs });
    return true;
  }

  /**
   * Move a tab from one panel to another
   * @param tabId The tab to move
   * @param targetPanelId The panel to move the tab to
   * @param targetIndex Optional index where to insert the tab in the target panel
   * @returns boolean True if the operation was successful
   */
  moveTab(tabId: string, targetPanelId: string, targetIndex?: number): boolean {
    try {
      // Get the tab and determine source panel
      const tab = this.getTab(tabId);

      const sourcePanelId = tab.parent;
      if (!sourcePanelId) {
        throw new Error(`Tab ${tabId} has no parent panel`);
      }

      // Get the source and target panels
      const sourcePanel = this.getPanel(sourcePanelId);
      const targetPanel = this.getPanel(targetPanelId);

      // Check if the tab is in the source panel
      if (!sourcePanel.tabs.includes(tabId)) {
        throw new Error(`Tab ${tabId} is not in source panel ${sourcePanelId}`);
      }

      // Create copies of the tab arrays
      const sourceTabs = [...sourcePanel.tabs];
      const targetTabs = [...targetPanel.tabs];

      // Remove the tab from the source panel
      const sourceIndex = sourceTabs.indexOf(tabId);
      sourceTabs.splice(sourceIndex, 1);

      // Find a new active tab for the source panel if needed
      let newSourceActiveTab = sourcePanel.activeTab;
      if (sourcePanel.activeTab === tabId) {
        // Get the next tab, or the previous one if there is no next
        newSourceActiveTab = sourceTabs.length > 0 ? sourceTabs[Math.min(sourceIndex, sourceTabs.length - 1)] : null;
      }

      // Add the tab to the target panel at the specified index or at the end
      if (targetIndex !== undefined && targetIndex >= 0 && targetIndex <= targetTabs.length) {
        targetTabs.splice(targetIndex, 0, tabId);
      } else {
        // Append to the end
        targetTabs.push(tabId);
      }

      // Update the tab's parent reference
      this.updateTile(tabId, { parent: targetPanelId });

      // Update the source panel
      this.updateTile(sourcePanelId, {
        tabs: sourceTabs,
        activeTab: newSourceActiveTab
      });

      // Update the target panel and make the moved tab active
      this.updateTile(targetPanelId, {
        tabs: targetTabs,
        activeTab: tabId
      });

      // Set the target panel as active
      this.setActivePanel(targetPanelId);

      // Check if source panel is now empty and should be cleaned up
      this.cleanupContainers(sourcePanelId);

      return true;
    } catch (error) {
      console.error('Error in moveTab:', error);
      return false;
    }
  }

  /**
   * Split a panel to create a new layout
   * @param tabId The tab to move to the new panel
   * @param targetPanelId The panel being split
   * @param direction The direction to split ('top', 'right', 'bottom', 'left')
   * @returns boolean True if the split operation was successful
   */
  splitPanel(
    tabId: string,
    targetPanelId: string,
    direction: 'top' | 'right' | 'bottom' | 'left'
  ): boolean {
    try {
      // Get the tab and determine source panel
      const tab = this.getTab(tabId);
      const sourcePanelId = tab.parent;

      if (!sourcePanelId) {
        throw new Error(`Tab ${tabId} has no parent panel`);
      }

      // Get the source and target panels
      const sourcePanel = this.getPanel(sourcePanelId);
      const targetPanel = this.getPanel(targetPanelId);

      // Check if the tab is in the source panel
      if (!sourcePanel.tabs.includes(tabId)) {
        throw new Error(`Tab ${tabId} is not in source panel ${sourcePanelId}`);
      }

      // Prevent splitting a panel with its only tab
      if (sourcePanelId === targetPanelId && sourcePanel.tabs.length === 1) {
        console.warn('Cannot split a panel with its only tab');
        return false;
      }

      // Find the column containing the target panel
      const targetParentId = targetPanel.parent;
      if (!targetParentId) {
        throw new Error(`Target panel ${targetPanelId} has no parent`);
      }

      const parentColumn = this.getColumn(targetParentId);

      // Different approach based on split direction
      if (direction === 'left' || direction === 'right') {
        // Horizontal split: Find the row containing the column with the target panel
        const rowId = parentColumn.parent;
        if (!rowId) {
          throw new Error(`Column ${parentColumn.id} has no parent row`);
        }

        const row = this.getRow(rowId);

        // Find the index of the current column in the row
        const columnIndex = row.columns.indexOf(parentColumn.id);
        if (columnIndex === -1) {
          throw new Error(`Column ${parentColumn.id} not found in row ${rowId}`);
        }

        // Calculate new width for existing and new column (50/50 split)
        const newWidth = parentColumn.width / 2;

        // Update existing column width
        this.updateTile(parentColumn.id, { width: newWidth });

        // Create a new column
        const newColumnId = this.addTile<TileColumn>({
          type: 'column',
          parent: rowId,
          width: newWidth,
          child: undefined
        });

        // Create a new panel in the new column
        const newPanelId = this.addTile<TilePanel>({
          type: 'panel',
          parent: newColumnId,
          tabs: [],
          activeTab: null
        });

        // Update new column with the new panel
        this.updateTile(newColumnId, { child: newPanelId });

        // Update row with the new column in the correct position (left or right of existing)
        const newColumns = [...row.columns];
        if (direction === 'right') {
          // Add after the current column
          newColumns.splice(columnIndex + 1, 0, newColumnId);
        } else {
          // Add before the current column
          newColumns.splice(columnIndex, 0, newColumnId);
        }

        this.updateTile(rowId, { columns: newColumns });

        // Move the tab to the new panel
        return this.moveTab(tabId, newPanelId);

      } else if (direction === 'top' || direction === 'bottom') {
        // Vertical split

        // Check if the column already contains a grid
        let gridId: string;

        if (parentColumn.child === targetPanelId) {
          // Column directly contains the panel, need to create a grid

          // Create new grid
          gridId = this.addTile<TileGrid>({
            type: 'grid',
            parent: parentColumn.id,
            rows: []
          });

          // Create two rows for the existing panel and the new one
          const rowHeight = 50; // 50% each

          // Create rows in appropriate order based on direction
          const firstRowId = this.addTile<TileRow>({
            type: 'row',
            parent: gridId,
            height: rowHeight,
            columns: []
          });

          const secondRowId = this.addTile<TileRow>({
            type: 'row',
            parent: gridId,
            height: rowHeight,
            columns: []
          });

          // Create a column for the existing panel
          const existingPanelColumnId = this.addTile<TileColumn>({
            type: 'column',
            parent: direction === 'top' ? secondRowId : firstRowId,
            width: 100, // 100% of row width
            child: targetPanelId
          });

          // Create a column for the new panel
          const newPanelColumnId = this.addTile<TileColumn>({
            type: 'column',
            parent: direction === 'top' ? firstRowId : secondRowId,
            width: 100, // 100% of row width
            child: undefined
          });

          // Update rows with their columns
          this.updateTile(firstRowId, {
            columns: [direction === 'top' ? newPanelColumnId : existingPanelColumnId]
          });

          this.updateTile(secondRowId, {
            columns: [direction === 'top' ? existingPanelColumnId : newPanelColumnId]
          });

          // Update grid with rows
          this.updateTile(gridId, {
            rows: [firstRowId, secondRowId]
          });

          // Create a new panel
          const newPanelId = this.addTile<TilePanel>({
            type: 'panel',
            parent: newPanelColumnId,
            tabs: [],
            activeTab: null
          });

          // Update column with new panel
          this.updateTile(newPanelColumnId, { child: newPanelId });

          // Update target panel parent to point to its new column
          this.updateTile(targetPanelId, { parent: existingPanelColumnId });

          // Update column to point to the grid instead of directly to the panel
          this.updateTile(parentColumn.id, { child: gridId });

          // Move the tab to the new panel
          return this.moveTab(tabId, newPanelId);

        } else {
          // Column already contains a grid or something else, need to handle differently
          const existingChild = parentColumn.child;
          if (!existingChild) {
            throw new Error(`Column ${parentColumn.id} has no child`);
          }

          try {
            // Try to get the child as a grid
            const grid = this.getGrid(existingChild);
            gridId = existingChild;

            // Find the row containing the target panel
            let targetRowId: string | null = null;
            let targetRow: TileRow | null = null;

            for (const rowId of grid.rows) {
              try {
                const row = this.getRow(rowId);

                // Check if any column in this row contains the target panel
                for (const colId of row.columns) {
                  try {
                    const col = this.getColumn(colId);
                    if (col.child === targetPanelId) {
                      targetRowId = rowId;
                      targetRow = row;
                      break;
                    }
                  } catch (error) {
                    console.error(`Error checking column ${colId}:`, error);
                    // Continue to next column
                  }
                }

                if (targetRowId) break;
              } catch (error) {
                console.error(`Error checking row ${rowId}:`, error);
                // Continue to next row
              }
            }

            if (!targetRowId || !targetRow) {
              throw new Error(`Could not find row containing panel ${targetPanelId}`);
            }

            // Calculate height for the new row
            const rowIndex = grid.rows.indexOf(targetRowId);
            const newHeight = targetRow.height / 2;

            // Update existing row height
            this.updateTile(targetRowId, { height: newHeight });

            // Create a new row
            const newRowId = this.addTile<TileRow>({
              type: 'row',
              parent: gridId,
              height: newHeight,
              columns: []
            });

            // Create a new column in the row
            const newColumnId = this.addTile<TileColumn>({
              type: 'column',
              parent: newRowId,
              width: 100, // 100% of the row
              child: undefined
            });

            // Create a new panel in the column
            const newPanelId = this.addTile<TilePanel>({
              type: 'panel',
              parent: newColumnId,
              tabs: [],
              activeTab: null
            });

            // Update column with panel
            this.updateTile(newColumnId, { child: newPanelId });

            // Update row with column
            this.updateTile(newRowId, { columns: [newColumnId] });

            // Update grid with new row in the right position
            const newRows = [...grid.rows];
            if (direction === 'bottom') {
              // Add after the current row
              newRows.splice(rowIndex + 1, 0, newRowId);
            } else {
              // Add before the current row
              newRows.splice(rowIndex, 0, newRowId);
            }

            this.updateTile(gridId, { rows: newRows });

            // Move the tab to the new panel
            return this.moveTab(tabId, newPanelId);
          } catch (error) {
            // The child is not a grid
            console.error(`Error handling existing child ${existingChild}:`, error);
            return false;
          }
        }
      }

      return false;
    } catch (error) {
      console.error('Error in splitPanel:', error);
      return false;
    }
  }

  /**
   * Recursively checks and cleans up empty containers
   * Traverses up the hierarchy to remove unnecessary container structures
   */
  cleanupContainers(tileId: string): void {
    const tile = this.getTile(tileId);
    if (!tile) return;

    let parentId = tile.parent;

    if (tile.dontClean) {
      if (parentId) {
        // If the tile is marked as dontClean, we don't try to clean it but 
        // go up the hierarchy in case if the parent's parent needs cleaning.
        this.cleanupContainers(parentId);
      }
      return;
    }

    let shouldRemove = false;

    // Check if the tile should be removed based on its type
    switch (tile.type) {
      case 'panel':
        // Remove the panel if it has no tabs
        const panel = tile as TilePanel;
        shouldRemove = panel.tabs.length === 0;
        break;

      case 'column':
        // Remove the column if it has no valid child
        const column = tile as TileColumn;
        shouldRemove = !column.child || !this.tiles[column.child];

        // Redistribute the width to siblings if removing
        if (shouldRemove && parentId) {
          const row = this.getRow(parentId);
          const siblingColumns = row.columns.filter(id => id !== tileId);
          if (siblingColumns.length > 0) {
            this.redistributeWidths(siblingColumns, column.width);
          }

        }
        break;

      case 'row':
        // Remove the row if it has no columns
        const row = tile as TileRow;
        shouldRemove = row.columns.length === 0;

        // Redistribute the height to siblings if removing
        if (shouldRemove && parentId) {
          const grid = this.getGrid(parentId);
          const siblingRows = grid.rows.filter(id => id !== tileId);
          if (siblingRows.length > 0) {
            this.redistributeHeights(siblingRows, row.height);
          }

        }
        break;

      case 'grid':
        // Remove the grid if it has no rows and is not the root
        const grid = tile as TileGrid;
        shouldRemove = grid.rows.length === 0 && !!grid.parent;

        // Check if we can simplify the grid hierarchy (for non root grids)
        if (!shouldRemove && grid.parent) {
          // In case if we have a grid with a single row and a single column, we can remove 
          // the grid and replace it with the only child of the column from that grid.
          if (grid.rows.length === 1) {
            const row = this.getRow(grid.rows[0]);
            // Just one row with a single column
            if (row.columns.length === 1) {
              const column = this.getColumn(row.columns[0]);
              const child = column.child;
              if (child) {
                const parentColumn = this.getColumn(grid.parent);
                this.updateTile(parentColumn.id, { child: child });
                this.updateTile(child, { parent: parentColumn.id });
                this.updateTile(grid.id, { rows: [] });
                this.removeTile(grid.id);
              }
            }
          }
        }

        break;
    }

    // Remove the tile if necessary and continue cleanup with parent
    if (shouldRemove) {
      this.removeTile(tileId);
    }

    if (parentId) {
      this.cleanupContainers(parentId);
    }
  }

  /**
   * Redistributes width proportionally among columns
   */
  redistributeWidths(columnIds: string[], availableWidth: number): void {
    if (columnIds.length === 0 || availableWidth <= 0) return;

    const columns = columnIds.map(id => this.getTile<TileColumn>(id)).filter(Boolean) as TileColumn[];
    if (columns.length === 0) return;

    const totalExistingWidth = columns.reduce((sum, col) => sum + col.width, 0);
    if (totalExistingWidth <= 0) return; // Prevent division by zero

    // Distribute proportionally
    columns.forEach(column => {
      const proportion = column.width / totalExistingWidth;
      const newWidth = column.width + (availableWidth * proportion);
      this.updateTile(column.id, { width: newWidth });
    });
  }

  /**
   * Redistributes height proportionally among rows
   */
  redistributeHeights(rowIds: string[], availableHeight: number): void {
    if (rowIds.length === 0 || availableHeight <= 0) return;

    const rows = rowIds.map(id => this.getTile<TileRow>(id)).filter(Boolean) as TileRow[];
    if (rows.length === 0) return;

    const totalExistingHeight = rows.reduce((sum, row) => sum + row.height, 0);
    if (totalExistingHeight <= 0) return; // Prevent division by zero

    // Distribute proportionally
    rows.forEach(row => {
      const proportion = row.height / totalExistingHeight;
      const newHeight = row.height + (availableHeight * proportion);
      this.updateTile(row.id, { height: newHeight });
    });
  }

  /**
   * Reset the state
   */
  resetState(): void {
    // With runes we can directly assign
    this.tiles = {};
    this.activePanel = null;
    this.focusedActiveTab = null;
    this.rootGridId = this.addGrid();
  }

  /**
   * Adds a grid to the layout
   * @param parentId Optional parent column ID
   * @returns ID of the new grid
   * @throws Error if parent hierarchy rules are violated
   */
  addGrid(parentId?: string | null): string {
    if (parentId) {
      const parent = this.getTile(parentId);
      if (!parent) {
        throw new Error(`Parent tile with ID ${parentId} not found`);
      }

      // Validate parent hierarchy rules
      if (parent.type === 'column') {
        // A grid can be added to a column
      } else {
        throw new Error(`Cannot add a grid to a parent of type ${parent.type}. Grids can only be children of columns.`);
      }
    }

    const gridId = this.addTile({
      type: 'grid',
      parent: parentId || null,
      rows: []
    });

    // If parent is a column, update the column's child reference
    if (parentId) {
      const parent = this.getTile(parentId);
      if (parent && parent.type === 'column') {
        this.updateTile(parentId, {
          child: gridId
        });
      }
    }

    return gridId;
  }

  /**
   * Adds a row to a grid
   * @param parentId ID of the parent grid
   * @param height Height of the row as a percentage
   * @returns ID of the new row
   * @throws Error if parent hierarchy rules are violated
   */
  addRow(parentId: string, height: number = 100): string {
    const parent = this.getTile(parentId);
    if (!parent) {
      throw new Error(`Parent tile with ID ${parentId} not found`);
    }

    // Validate parent hierarchy rules
    if (parent.type !== 'grid') {
      throw new Error(`Cannot add a row to a parent of type ${parent.type}. Rows can only be children of grids.`);
    }

    const grid = parent as TileGrid;

    // Create the row
    const rowId = this.addTile({
      type: 'row',
      parent: parentId,
      columns: [],
      height
    });

    // Update grid's rows
    this.updateTile(parentId, {
      rows: [...grid.rows, rowId]
    });

    return rowId;
  }

  /**
   * Adds a column to a row
   * @param parentId ID of the parent row
   * @param width Width of the column as a percentage
   * @returns ID of the new column
   * @throws Error if parent hierarchy rules are violated
   */
  addColumn(parentId: string, width: number = 100): string {
    const parent = this.getTile(parentId);
    if (!parent) {
      throw new Error(`Parent tile with ID ${parentId} not found`);
    }

    // Validate parent hierarchy rules
    if (parent.type !== 'row') {
      throw new Error(`Cannot add a column to a parent of type ${parent.type}. Columns can only be children of rows.`);
    }

    const row = parent as TileRow;

    // Create the column
    const columnId = this.addTile({
      type: 'column',
      parent: parentId,
      child: '',
      width
    });

    // Update row's columns
    this.updateTile(parentId, {
      columns: [...row.columns, columnId]
    });

    return columnId;
  }

  /**
   * Adds a panel to a column
   * @param parentId ID of the parent column
   * @returns ID of the new panel
   * @throws Error if parent hierarchy rules are violated
   */
  addPanel(parentId: string): string {
    const parent = this.getTile(parentId);
    if (!parent) {
      throw new Error(`Parent tile with ID ${parentId} not found`);
    }

    // Validate parent hierarchy rules
    if (parent.type !== 'column') {
      throw new Error(`Cannot add a panel to a parent of type ${parent.type}. Panels can only be children of columns.`);
    }

    const column = parent as TileColumn;

    // Check if column already has a child
    if (column.child && this.getTile(column.child)) {
      throw new Error(`Column ${parentId} already has a child. Remove it first.`);
    }

    // Create the panel
    const panelId = this.addTile({
      type: 'panel',
      parent: parentId,
      tabs: [],
      activeTab: null
    });

    // Update column's child
    this.updateTile(parentId, {
      child: panelId
    });

    return panelId;
  }

  /**
   * Adds a tab to a panel
   * @param panelId ID of the parent panel
   * @param name Name of the tab
   * @param setActive Whether to set this tab as active
   * @returns ID of the new tab
   * @throws Error if parent hierarchy rules are violated
   */
  private addTabToPanel(panel: TilePanel, name: string, setActive: boolean = true): string {
    // Create the tab
    const tabId = this.addTile({
      type: 'tab',
      parent: panel.id,
      name,
      content: ''
    });

    // Create content for the tab
    const contentId = this.addTile({
      type: 'content',
      parent: tabId,
    });

    // Update tab with its content
    this.updateTile(tabId, {
      content: contentId
    });

    // Update panel's tabs
    const updatedTabs = [...panel.tabs, tabId];
    this.updateTile(panel.id, {
      tabs: updatedTabs,
      ...(setActive ? { activeTab: tabId } : {})
    });

    return tabId;
  }

  /**
   * Adds a tab to a panel. If the parent is a grid, it will be added to the first row and column of the grid.
   * @param parentId ID of the parent container (grid, column, or panel)
   * @param name Name of the tab
   * @param setActive Whether to set this tab as active
   * @returns ID of the new tab
   */
  addTab(parentId: string, name: string, setActive: boolean = true): string {
    const parent = this.getTile(parentId);
    if (!parent) {
      throw new Error(`Parent tile with ID ${parentId} not found`);
    }

    switch (parent.type) {
      case 'panel':
        // Panel is the direct container for tabs, add directly
        return this.addTabToPanel(parent as TilePanel, name, setActive);

      case 'column':
        // Need to check if column already has a child
        const column = parent as TileColumn;
        let panelId = '';

        if (column.child) {
          // If column already has a child, it might be a panel or a grid
          const child = this.getTile(column.child);
          if (!child) {
            // Child reference exists but tile doesn't, create a new panel
            panelId = this.addPanel(column.id);
          } else if (child.type === 'panel') {
            // Column already has a panel, use it
            panelId = child.id;
          } else if (child.type === 'grid') {
            // Column has a grid, find or create a panel in the grid
            panelId = this.findOrCreatePanelInGrid(child.id);
          } else {
            // Column has some other child (should be a content), replace with a panel
            this.removeTile(child.id);
            panelId = this.addPanel(column.id);
          }
        } else {
          // Column has no child, add a panel
          panelId = this.addPanel(column.id);
        }

        return this.addTabToPanel(this.getPanel(panelId), name, setActive);

      case 'row':
        // For rows, we need to find or create a column to contain our panel
        const row = parent as TileRow;
        let columnId: string;
        
        if (row.columns.length > 0) {
          // Use the last column in the row
          columnId = row.columns[row.columns.length - 1];
        } else {
          // Create a new column
          columnId = this.addColumn(row.id);
        }
        
        // Recursively call addTab with the column ID
        return this.addTab(columnId, name, setActive);

      case 'grid':
        // Find or create a panel in the grid
        const panelInGridId = this.findOrCreatePanelInGrid(parentId);
        return this.addTabToPanel(this.getPanel(panelInGridId), name, setActive);

      default:
        throw new Error(`Cannot add a tab to a parent of type ${parent.type}. Use a panel, column, row, or grid.`);
    }
  }

  /**
   * Finds an existing panel in a grid or creates a new one
   * @param gridId ID of the grid
   * @returns ID of a panel in the grid
   */
  private findOrCreatePanelInGrid(gridId: string): string {
    const grid = this.getGrid(gridId);

    // Try to find an existing panel
    for (const rowId of grid.rows) {
      const row = this.getRow(rowId);
      for (const columnId of row.columns) {
        const column = this.getColumn(columnId);
        if (column.child) {
          const child = this.getTile(column.child);
          if (child && child.type === 'panel') {
            return child.id;
          }
        }
      }
    }

    // No panel found, create a new row, column, and panel
    let rowId: string;
    if (grid.rows.length === 0) {
      // No rows exist, create one
      rowId = this.addRow(gridId);
    } else {
      // Use the last row
      rowId = grid.rows[grid.rows.length - 1];
    }

    // Create a column in the row
    const columnId = this.addColumn(rowId);

    // Create a panel in the column
    return this.addPanel(columnId);
  }

  /**
   * Ads a new tab in the active panel
   * @param name Name of the tab
   * @returns ID of the new tab, or null if no active panel exists
   */
  addTabInActivePanel(name: string, setActive: boolean = true): string | null {
    const activePanel = this.getActivePanelTile();
    if (!activePanel) return null;

    const tabId = this.addTabToPanel(activePanel, name, setActive);

    return tabId;
  }

  /**
   * Serialize the layout to JSON
   */
  serializeLayout(): string {
    // Get all tiles as an array
    const tilesArray = Object.values(this.tiles);

    // Add metadata for focused tab
    const metadata = {
      focusedActiveTab: this.focusedActiveTab
    };

    return JSON.stringify({
      tiles: tilesArray,
      metadata
    });
  }

  /**
   * Deserialize a layout from JSON
   */
  deserializeLayout(json: string): boolean {
    try {
      // Try to parse as new format first (with metadata)
      try {
        const parsed = JSON.parse(json);
        if (parsed && typeof parsed === 'object' && 'tiles' in parsed && Array.isArray(parsed.tiles)) {
          // Reset current state
          this.resetState();

          // Set tiles from array
          parsed.tiles.forEach((tile: Tile) => {
            this.tiles[tile.id] = tile;
          });

          // Find the root grid
          this.rootGridId = this.findRootGridId();

          // Restore metadata
          if (parsed.metadata && 'focusedActiveTab' in parsed.metadata) {
            const focusedTabId = parsed.metadata.focusedActiveTab;
            if (focusedTabId && this.getTile<TileTab>(focusedTabId)) {
              this.focusedActiveTab = focusedTabId;
            }
          }

          return true;
        }
      } catch (e) {
        // Fall back to older formats
      }

      // Try to parse as array format (without metadata)
      try {
        const parsedTiles = JSON.parse(json) as Tile[];
        if (Array.isArray(parsedTiles)) {
          // Reset current state
          this.resetState();

          // Set tiles from array
          parsedTiles.forEach(tile => {
            this.tiles[tile.id] = tile;
          });

          // Find the root grid
          this.rootGridId = this.findRootGridId();

          // Try to find a suitable tab to focus
          this.findAndSetDefaultFocusedTab();

          return true;
        }
      } catch (e) {
        // Fall back to record format if array parsing fails
        console.error('Failed to deserialize layout:', e);
        return false;
      }

      return false;
    } catch (e) {
      console.error('Failed to deserialize layout:', e);
      return false;
    }
  }

  /**
   * Find and set a default focused tab when none is specified
   * Used after deserializing older layouts
   */
  private findAndSetDefaultFocusedTab(): void {
    // If active panel exists, try to use its active tab
    if (this.activePanel) {
      const panel = this.getTile<TilePanel>(this.activePanel);
      if (panel && panel.activeTab) {
        this.focusedActiveTab = panel.activeTab;
        return;
      }
    }

    // Otherwise find the first panel with an active tab
    const panelsWithActiveTabs = Object.values(this.tiles)
      .filter((tile): tile is TilePanel =>
        tile.type === 'panel' && tile.activeTab !== null && !!this.getTile(tile.activeTab));

    if (panelsWithActiveTabs.length > 0 && panelsWithActiveTabs[0].activeTab) {
      this.focusedActiveTab = panelsWithActiveTabs[0].activeTab;
    }
  }

  /**
   * Close a tab and remove it
   * @param tabId The ID of the tab to close
   * @returns True if successful
   */
  closeTab(tabId: string): boolean {
    try {
      const tab = this.getTile<TileTab>(tabId);
      if (!tab || tab.type !== 'tab') return false;

      const panelId = tab.parent;
      if (!panelId) return false;

      const panel = this.getTile<TilePanel>(panelId);
      if (!panel || panel.type !== 'panel') return false;

      // Find index of tab to remove
      const tabIndex = panel.tabs.indexOf(tabId);
      if (tabIndex === -1) return false;

      // If we're closing the focused tab, we need to find a new tab to focus
      let newFocusedTab: string | null = null;
      if (this.focusedActiveTab === tabId) {
        // First try to find another tab in the same panel
        if (panel.tabs.length > 1) {
          // Use the same logic as for activeTab - get next or previous tab
          const newTabIndex = Math.min(tabIndex, panel.tabs.length - 2);
          newFocusedTab = panel.tabs[newTabIndex];
          if (newFocusedTab === tabId) {
            // Extra safety check
            newFocusedTab = panel.tabs.find(t => t !== tabId) || null;
          }
        } else {
          // Look for a tab in another panel
          const otherPanels = Object.values(this.tiles)
            .filter((tile): tile is TilePanel =>
              tile.type === 'panel' && tile.id !== panelId && tile.tabs.length > 0);

          // Find the first panel with tabs and use its active tab
          const otherPanel = otherPanels[0];
          if (otherPanel && otherPanel.activeTab) {
            newFocusedTab = otherPanel.activeTab;
          }
        }
      }

      // Remove tab
      this.removeTile(tabId);

      // Update panel's tabs array
      const newTabs = [...panel.tabs];
      newTabs.splice(tabIndex, 1);

      // If closed tab was active, activate another tab
      let newActiveTab = panel.activeTab;
      if (panel.activeTab === tabId) {
        if (newTabs.length > 0) {
          // Activate either the tab at the same index, or the last tab
          const newActiveIndex = Math.min(tabIndex, newTabs.length - 1);
          newActiveTab = newTabs[newActiveIndex];
        } else {
          newActiveTab = null;
        }
      }

      // Update panel
      this.updateTile(panelId, {
        tabs: newTabs,
        activeTab: newActiveTab
      });

      // Update the focused tab if needed
      if (newFocusedTab) {
        this.focusedActiveTab = newFocusedTab;
      }

      // Clean up empty containers
      this.cleanupContainers(panelId);

      return true;
    } catch (error) {
      console.error('Error closing tab:', error);
      return false;
    }
  }

  /**
   * Set or update the theme
   */
  setTheme(theme: TtabsTheme): void {
    console.log('Setting theme:', theme.name);

    // Resolve the theme to handle inheritance
    const resolvedTheme = resolveTheme(theme);

    // Ensure we're making a full replacement to trigger reactivity
    this.theme = { ...resolvedTheme };

    console.log('Theme updated:', this.theme.name);
  }
}