/**
 * ExtensionDashboard - Tabbed layout for the Extension Control Center.
 *
 * Layout:
 * - Top: Horizontal tab bar for provider selection
 * - Body: 2-column grid (inventory list | preview panel)
 *
 * Navigation:
 * - TAB/Shift+TAB: Cycle through provider tabs
 * - Up/Down/j/k: Navigate list
 * - Space: Toggle selected item (or master switch)
 * - Esc: Close dashboard (clears search first if active)
 */
import {
	type Component,
	Container,
	matchesKey,
	padding,
	Spacer,
	Text,
	truncateToWidth,
	visibleWidth,
	wrapTextWithAnsi,
} from './stubs/tui/index.ts';
import { Settings } from "../../../config/settings";
import { DynamicBorder } from "../../../modes/components/dynamic-border";
import { theme } from "../../../modes/theme/theme";
import { matchesAppInterrupt } from "../../../modes/utils/keybinding-matchers";
import { ExtensionList } from "./extension-list";
import { InspectorPanel } from "./inspector-panel";
import {
	applyDisabledExtensionsToState,
	applyFilter,
	createInitialState,
	filterByProvider,
	refreshState,
	toggleProvider,
} from "./state-manager";
import type { DashboardState } from "./types";

const EXT_FOOTER = " ↑/↓: navigate  Space: toggle  ←/→: provider  Esc: close";

export class ExtensionDashboard extends Container {
	#state!: DashboardState;
	#mainList!: ExtensionList;
	#inspector!: InspectorPanel;
	#refreshToken = 0;
	#builtRows = -1;
	#builtCols = -1;

	onClose?: () => void;
	onRequestRender?: () => void;

	private constructor(
		private readonly cwd: string,
		private readonly settings: Settings | null,
		private readonly terminalHeight: number,
	) {
		super();
	}

	static async create(
		cwd: string,
		settings: Settings | null = null,
		terminalHeight?: number,
	): Promise<ExtensionDashboard> {
		const dashboard = new ExtensionDashboard(cwd, settings, terminalHeight ?? process.stdout.rows ?? 24);
		await dashboard.#init();
		return dashboard;
	}

	async #init(): Promise<void> {
		const sm = this.settings ?? (await Settings.init());
		const disabledIds = sm ? ((sm.get("disabledExtensions") as string[]) ?? []) : [];
		this.#state = await createInitialState(this.cwd, disabledIds);

		// Calculate max visible items based on terminal height
		// Reserve ~10 lines for header, tabs, help text, borders
		const maxVisible = this.#maxVisibleItems();

		// Create main list - always focused
		this.#mainList = new ExtensionList(
			this.#state.searchFiltered,
			{
				onSelectionChange: ext => {
					this.#state.selected = ext;
					this.#inspector.setExtension(ext);
				},
				onToggle: (extensionId, enabled) => {
					this.#handleExtensionToggle(extensionId, enabled);
				},
				onMasterToggle: providerId => {
					this.#handleProviderToggle(providerId);
				},
				masterSwitchProvider: this.#getActiveProviderId(),
			},
			maxVisible,
		);
		this.#mainList.setFocused(true);

		// Create inspector
		this.#inspector = new InspectorPanel();
		if (this.#state.selected) {
			this.#inspector.setExtension(this.#state.selected);
		}

		this.#buildLayout();
	}

	#getActiveProviderId(): string | null {
		const tab = this.#state.tabs[this.#state.activeTabIndex];
		return tab && tab.id !== "all" ? tab.id : null;
	}

	/** Live terminal height so the dashboard tracks resize while open. */
	#terminalRows(): number {
		return process.stdout.rows || this.terminalHeight || 24;
	}

	#uiWidth(): number {
		return Math.max(20, process.stdout.columns || 80);
	}

	#footerLines(): number {
		return Math.max(1, wrapTextWithAnsi(theme.fg("dim", EXT_FOOTER), this.#uiWidth()).length);
	}

	/** Height budget for the two-column body, sized to the live terminal. */
	#computeBodyHeight(): number {
		// Chrome: top border + title + tab bar + spacer (4), then spacer + footer + bottom border.
		const chrome = 4 + 1 + this.#footerLines() + 1;
		return Math.max(5, this.#terminalRows() - chrome);
	}

	#maxVisibleItems(): number {
		// List chrome inside the body: search line, blank line, scroll indicator.
		return Math.max(3, this.#computeBodyHeight() - 3);
	}

	override render(width: number): readonly string[] {
		// Rebuild when terminal geometry changes so the full-screen overlay
		// re-fits on resize.
		if (this.#terminalRows() !== this.#builtRows || this.#uiWidth() !== this.#builtCols) {
			this.#buildLayout();
		}
		const lines = super.render(width);
		// Pad to the full viewport so the dashboard covers the screen instead of
		// letting the transcript peek through below it. Copy before padding — the
		// container's render result is component-owned and must not be mutated.
		const rows = this.#terminalRows();
		if (lines.length >= rows) return lines;
		const padded = lines.slice();
		while (padded.length < rows) padded.push("");
		return padded;
	}

	#buildLayout(): void {
		this.clear();

		// Top border
		this.addChild(new DynamicBorder());

		// Title
		this.addChild(new Text(theme.bold(theme.fg("accent", " Extension Control Center")), 0, 0));

		// Tab bar
		this.addChild(new Text(this.#renderTabBar(), 0, 0));
		this.addChild(new Spacer(1));

		// 2-column body sized to fill the live terminal viewport.
		const bodyMaxHeight = this.#computeBodyHeight();
		this.#mainList.setMaxVisible(this.#maxVisibleItems());
		this.addChild(new TwoColumnBody(this.#mainList, this.#inspector, bodyMaxHeight));

		this.addChild(new Spacer(1));
		this.addChild(new Text(theme.fg("dim", EXT_FOOTER), 0, 0));

		// Bottom border
		this.addChild(new DynamicBorder());
		this.#builtRows = this.#terminalRows();
		this.#builtCols = this.#uiWidth();
	}

	#renderTabBar(): string {
		const parts: string[] = [" "];

		for (let i = 0; i < this.#state.tabs.length; i++) {
			const tab = this.#state.tabs[i];
			const isActive = i === this.#state.activeTabIndex;
			const isEmpty = tab.count === 0 && tab.id !== "all";
			const isDisabled = !tab.enabled && tab.id !== "all";

			// Build label with count
			let label = tab.label;
			if (tab.count > 0) {
				label += ` (${tab.count})`;
			}

			const displayLabel = isDisabled ? `${theme.status.disabled} ${label}` : label;

			if (isActive) {
				// Active tab: background highlight
				parts.push(theme.bg("selectedBg", ` ${displayLabel} `));
			} else if (isDisabled) {
				// Disabled provider: dim
				parts.push(theme.fg("dim", ` ${displayLabel} `));
			} else if (isEmpty) {
				// Empty enabled provider: very dim, unselectable
				parts.push(theme.fg("dim", ` ${label} `));
			} else {
				// Normal enabled provider
				parts.push(theme.fg("muted", ` ${label} `));
			}
		}

		return parts.join("");
	}

	#handleProviderToggle(providerId: string): void {
		toggleProvider(providerId);
		void this.#refreshFromState();
	}

	#handleExtensionToggle(extensionId: string, enabled: boolean): void {
		const sm = this.settings ?? Settings.instance;
		if (!sm) return;

		const disabled = ((sm.get("disabledExtensions") as string[]) ?? []).slice();
		if (enabled) {
			const index = disabled.indexOf(extensionId);
			if (index !== -1) {
				disabled.splice(index, 1);
				sm.set("disabledExtensions", disabled);
			}
		} else {
			if (!disabled.includes(extensionId)) {
				disabled.push(extensionId);
				sm.set("disabledExtensions", disabled);
			}
		}

		this.#applyDisabledExtensions(disabled);
		void this.#refreshFromState();
	}

	async #refreshFromState(): Promise<void> {
		const refreshToken = ++this.#refreshToken;
		// Remember current tab ID before refresh
		const currentTabId = this.#state.tabs[this.#state.activeTabIndex]?.id;

		const sm = this.settings ?? Settings.instance;
		const disabledIds = sm ? ((sm.get("disabledExtensions") as string[]) ?? []) : [];
		const nextState = await refreshState(this.#state, this.cwd, disabledIds);
		if (refreshToken !== this.#refreshToken) return;
		this.#state = nextState;

		// Find the same tab in the new (re-sorted) list
		if (currentTabId) {
			const newIndex = this.#state.tabs.findIndex(t => t.id === currentTabId);
			if (newIndex >= 0) {
				this.#state.activeTabIndex = newIndex;
			}
		}

		this.#mainList.setExtensions(this.#state.searchFiltered);
		this.#mainList.setMasterSwitchProvider(this.#getActiveProviderId());

		if (this.#state.selected) {
			this.#inspector.setExtension(this.#state.selected);
		}

		this.#buildLayout();
		this.onRequestRender?.();
	}

	#applyDisabledExtensions(disabledIds: string[]): void {
		this.#state = applyDisabledExtensionsToState(this.#state, disabledIds);
		this.#mainList.setExtensions(this.#state.searchFiltered);
		if (this.#state.selected) {
			this.#inspector.setExtension(this.#state.selected);
		}
		this.#buildLayout();
		this.onRequestRender?.();
	}

	#switchTab(direction: 1 | -1): void {
		const numTabs = this.#state.tabs.length;
		if (numTabs === 0) return;

		// Find next selectable tab (skip empty+enabled providers)
		let nextIndex = this.#state.activeTabIndex;
		for (let i = 0; i < numTabs; i++) {
			nextIndex = (nextIndex + direction + numTabs) % numTabs;
			const tab = this.#state.tabs[nextIndex];
			const isEmptyEnabled = tab.count === 0 && tab.enabled && tab.id !== "all";
			if (!isEmptyEnabled) break;
		}
		this.#state.activeTabIndex = nextIndex;

		// Re-filter for new tab
		const tab = this.#state.tabs[this.#state.activeTabIndex];
		this.#state.tabFiltered = filterByProvider(this.#state.extensions, tab.id);
		this.#state.searchFiltered = applyFilter(this.#state.tabFiltered, this.#state.searchQuery);
		this.#state.listIndex = 0;
		this.#state.scrollOffset = 0;
		this.#state.selected = this.#state.searchFiltered[0] ?? null;

		// Update list
		this.#mainList.setExtensions(this.#state.searchFiltered);
		this.#mainList.setMasterSwitchProvider(this.#getActiveProviderId());
		this.#mainList.resetSelection();

		if (this.#state.selected) {
			this.#inspector.setExtension(this.#state.selected);
		}

		this.#buildLayout();
	}

	handleInput(data: string): void {
		// Ctrl+C - close immediately
		if (matchesKey(data, "ctrl+c")) {
			this.onClose?.();
			return;
		}

		// Escape - clear search first, then close
		if (matchesAppInterrupt(data)) {
			if (this.#state.searchQuery.length > 0) {
				this.#state.searchQuery = "";
				this.#state.searchFiltered = this.#state.tabFiltered;
				this.#mainList.setExtensions(this.#state.searchFiltered);
				this.#mainList.clearSearch();
				this.#buildLayout();
				return;
			}
			this.onClose?.();
			return;
		}

		// Tab/Shift+Tab or Left/Right: Cycle through tabs
		if (matchesKey(data, "tab") || matchesKey(data, "right")) {
			this.#switchTab(1);
			return;
		}
		if (matchesKey(data, "shift+tab") || matchesKey(data, "left")) {
			this.#switchTab(-1);
			return;
		}

		// All other input goes to the list
		this.#mainList.handleInput(data);

		// Sync search query back to state
		const query = this.#mainList.getSearchQuery();
		if (query !== this.#state.searchQuery) {
			this.#state.searchQuery = query;
			this.#state.searchFiltered = applyFilter(this.#state.tabFiltered, query);
		}
	}
}

/**
 * Two-column body component for side-by-side rendering.
 */
class TwoColumnBody implements Component {
	constructor(
		private readonly leftPane: ExtensionList,
		private readonly rightPane: InspectorPanel,
		private readonly maxHeight: number,
	) {}

	render(width: number): readonly string[] {
		const leftWidth = Math.floor(width * 0.5);
		const rightWidth = Math.max(0, width - leftWidth - 3);

		const leftLines = this.leftPane.render(leftWidth);
		const rightLines = this.rightPane.render(rightWidth);

		// Fill the full body height so the dashboard reads as a full-screen view.
		const numLines = this.maxHeight;
		const combined: string[] = [];
		const separator = theme.fg("dim", ` ${theme.boxSharp.vertical} `);

		for (let i = 0; i < numLines; i++) {
			const left = truncateToWidth(leftLines[i] ?? "", leftWidth);
			const leftPadded = left + padding(Math.max(0, leftWidth - visibleWidth(left)));
			const right = truncateToWidth(rightLines[i] ?? "", rightWidth);
			combined.push(leftPadded + separator + right);
		}

		return combined;
	}

	invalidate(): void {
		this.leftPane.invalidate?.();
		this.rightPane.invalidate?.();
	}
}
