/**
 * Zustand store for UI state management.
 *
 * This store owns all UI state. The UI Server connects directly to the
 * Python backend via HTTP/WebSocket - no VS Code extension middleman.
 */

import { create } from 'zustand';
import { devtools, subscribeWithSelector } from 'zustand/middleware';
import { postMessage, getVsCodeApi } from '../api/vscodeApi';
import type {
  AppState,
  Project,
  Build,
  BuildTarget,
  PackageInfo,
  PackageDetails,
  StdLibItem,
  BOMData,
  Problem,
  ModuleDefinition,
  FileTreeNode,
  ProjectDependency,
  VariablesData,
  TestItem,
  TestRun,
} from '../types/build';
import type {
  ManufacturingWizardState,
  ManufacturingBuild,
  FileExportType,
  CostEstimate,
} from '../components/manufacturing/types';
import { DEFAULT_FILE_TYPES } from '../components/manufacturing/types';

const ERROR_TIMEOUT_MS = 8000;

let _readySignalled = false;

let installErrorTimeout: ReturnType<typeof setTimeout> | null = null;
let packagesErrorTimeout: ReturnType<typeof setTimeout> | null = null;
let bomErrorTimeout: ReturnType<typeof setTimeout> | null = null;
let variablesErrorTimeout: ReturnType<typeof setTimeout> | null = null;
let packageDetailsErrorTimeout: ReturnType<typeof setTimeout> | null = null;
let projectsErrorTimeout: ReturnType<typeof setTimeout> | null = null;
let atopileErrorTimeout: ReturnType<typeof setTimeout> | null = null;

const arraysEqual = (a: string[], b: string[]) => {
  if (a.length !== b.length) return false;
  return a.every((value, index) => value === b[index]);
};

// BroadcastChannel for cross-webview state synchronization
const _channel: BroadcastChannel | null = (() => {
  try {
    return new BroadcastChannel('atopile-store');
  } catch {
    return null;
  }
})();

// Restore last selection from VS Code webview state (survives reloads)
const persistedState = getVsCodeApi()?.getState() as {
  selectedProjectRoot?: string | null;
  selectedProjectName?: string | null;
  selectedTargetNames?: string[];
} | undefined;

// Initial state for the store
const initialState: AppState = {
  // Connection - start as true so we don't flash error screen during startup
  // WebSocket will set to false after 5s timeout if backend isn't ready
  isConnected: true,

  // Projects
  projects: [],
  isLoadingProjects: false,
  projectsError: null,
  selectedProjectRoot: persistedState?.selectedProjectRoot ?? null,
  selectedTargetNames: persistedState?.selectedTargetNames ?? [],
  migratingProjectRoots: [] as string[],
  migrationErrors: {} as Record<string, string>,

  // Builds
  builds: [],
  queuedBuilds: [],
  buildHistory: [],

  // Packages
  packages: [],
  isLoadingPackages: false,
  packagesError: null,
  installingPackageIds: [],
  installError: null,
  updatingDependencyIds: [],

  // Standard Library
  stdlibItems: [],
  isLoadingStdlib: false,

  // BOM
  bomData: null,
  isLoadingBom: false,
  bomError: null,

  // Package details
  selectedPackageDetails: null,
  isLoadingPackageDetails: false,
  packageDetailsError: null,

  // Build selection
  selectedBuildId: null,
  selectedBuildName: null,
  selectedProjectName: persistedState?.selectedProjectName ?? null,

  // Log viewer
  logViewerBuildId: null as string | null,

  // Sidebar UI
  expandedTargets: [],
  activeEditorFile: null,
  lastAtoFile: null,

  // Atopile configuration
  atopile: {
    // Actual running atopile info
    actualVersion: null as string | null,
    actualSource: null as string | null,  // 'explicit-path', 'from-setting', or 'default'
    actualBinaryPath: null as string | null,
    fromBranch: null as string | null,  // Git branch when installed via uv from git
    fromSpec: null as string | null,  // The pip/uv spec (for from-setting mode)
    // User selection state
    source: 'release' as 'release' | 'local',
    localPath: null as string | null,
    isInstalling: false,
    installProgress: null as { message: string; percent?: number } | null,
    error: null as string | null,
  },

  // Problems
  problems: [],
  isLoadingProblems: false,
  problemFilter: {
    levels: ['error', 'warning'],
    buildNames: [],
    stageIds: [],
  },

  // Developer mode - shows all log audiences instead of just 'user'
  developerMode: false,

  // Project modules
  projectModules: {},
  isLoadingModules: false,

  // Project files
  projectFiles: {},
  isLoadingFiles: false,

  // Project dependencies
  projectDependencies: {},
  isLoadingDependencies: false,

  // Project builds (for installed dependencies)
  projectBuilds: {},
  isLoadingBuilds: false,

  // Variables
  currentVariablesData: null,
  isLoadingVariables: false,
  variablesError: null,

  // Test Explorer
  collectedTests: [] as TestItem[],
  isLoadingTests: false,
  testsError: null as string | null,
  testCollectionErrors: {} as Record<string, string>,
  selectedTestNodeIds: [] as string[],
  testRun: {
    testRunId: null,
    isRunning: false,
  } as TestRun,
  testFilter: '',
  testPaths: 'test src',
  testMarkers: '',

  // Manufacturing Wizard
  manufacturingWizard: null as ManufacturingWizardState | null,
};

// Store actions interface
interface StoreActions {
  // Connection
  setConnected: (connected: boolean) => void;

  // Full state replacement (from WebSocket)
  replaceState: (state: Partial<AppState>) => void;

  // Projects
  setProjects: (projects: Project[]) => void;
  setLoadingProjects: (loading: boolean) => void;
  setProjectsError: (error: string | null) => void;
  selectProject: (projectRoot: string | null) => void;
  setSelectedTargets: (targetNames: string[]) => void;
  toggleTarget: (targetName: string) => void;
  toggleTargetExpanded: (targetName: string) => void;

  // Migration
  addMigratingProject: (projectRoot: string) => void;
  removeMigratingProject: (projectRoot: string) => void;
  setMigrationError: (projectRoot: string, error: string | null) => void;
  migrationErrors: Record<string, string>;

  // Builds
  setBuilds: (builds: Build[]) => void;
  setQueuedBuilds: (builds: Build[]) => void;
  setBuildHistory: (builds: Build[]) => void;
  selectBuild: (buildName: string | null) => void;
  selectBuildById: (buildId: string | null, buildName?: string | null) => void;

  // Log viewer
  setLogViewerBuildId: (buildId: string | null) => void;

  // Active editor file (from VS Code)
  setActiveEditorFile: (filePath: string | null) => void;
  setLastAtoFile: (filePath: string | null) => void;

  // Packages
  setPackages: (packages: PackageInfo[]) => void;
  updatePackageDownloads: (downloads: Record<string, number>) => void;
  setLoadingPackages: (loading: boolean) => void;
  setPackagesError: (error: string | null) => void;
  setPackageDetails: (details: PackageDetails | null) => void;
  clearPackageDetailsAndStartLoading: () => void;
  addInstallingPackage: (packageId: string) => void;
  removeInstallingPackage: (packageId: string) => void;
  clearInstallingPackages: () => void;
  setInstallError: (packageId: string, error: string | null) => void;
  addUpdatingDependency: (projectRoot: string, dependencyId: string) => void;
  removeUpdatingDependency: (projectRoot: string, dependencyId: string) => void;

  // Problems
  setProblems: (problems: Problem[]) => void;
  setLoadingProblems: (loading: boolean) => void;
  setDeveloperMode: (enabled: boolean) => void;

  // Standard Library
  setStdlibItems: (items: StdLibItem[]) => void;
  setLoadingStdlib: (loading: boolean) => void;

  // BOM
  setBomData: (data: BOMData | null) => void;
  setLoadingBom: (loading: boolean) => void;
  setBomError: (error: string | null) => void;

  // Variables
  setVariablesData: (data: VariablesData | null) => void;
  setLoadingVariables: (loading: boolean) => void;
  setVariablesError: (error: string | null) => void;

  // Atopile config
  setAtopileConfig: (update: Partial<AppState['atopile']>) => void;

  // Project data
  setProjectModules: (projectRoot: string, modules: ModuleDefinition[]) => void;
  setProjectFiles: (projectRoot: string, files: FileTreeNode[]) => void;
  setProjectDependencies: (projectRoot: string, deps: ProjectDependency[]) => void;
  setProjectBuilds: (projectRoot: string, builds: BuildTarget[]) => void;
  setLoadingModules: (loading: boolean) => void;
  setLoadingFiles: (loading: boolean) => void;
  setLoadingDependencies: (loading: boolean) => void;

  // Test Explorer
  setCollectedTests: (tests: TestItem[]) => void;
  setLoadingTests: (loading: boolean) => void;
  setTestsError: (error: string | null) => void;
  setTestCollectionErrors: (errors: Record<string, string>) => void;
  setTestFilter: (filter: string) => void;
  setTestPaths: (paths: string) => void;
  setTestMarkers: (markers: string) => void;
  toggleTestSelected: (nodeId: string) => void;
  selectAllTests: () => void;
  clearTestSelection: () => void;
  startTestRun: (testRunId: string) => void;
  completeTestRun: () => void;

  // Manufacturing Wizard
  openManufacturingWizard: (projectRoot: string, targets: string[]) => void;
  closeManufacturingWizard: () => void;
  setManufacturingStep: (step: 1 | 2 | 3) => void;
  toggleManufacturingBuild: (targetName: string) => void;
  updateManufacturingBuild: (targetName: string, update: Partial<ManufacturingBuild>) => void;
  setManufacturingGitStatus: (hasChanges: boolean, files: string[]) => void;
  dismissUncommittedWarning: () => void;
  setManufacturingExportDirectory: (directory: string) => void;
  toggleManufacturingFileType: (fileType: FileExportType) => void;
  setManufacturingCostEstimate: (estimate: CostEstimate | null) => void;
  setManufacturingQuantity: (quantity: number) => void;
  setManufacturingLoading: (key: 'gitStatus' | 'cost' | 'exporting', loading: boolean) => void;
  setManufacturingExportError: (error: string | null) => void;

  // Reset
  reset: () => void;
}

// Combined store type
type Store = AppState & StoreActions;

export const useStore = create<Store>()(
  subscribeWithSelector(
    devtools(
      (set) => ({
        ...initialState,

      // Connection
      setConnected: (connected) => set({ isConnected: connected }),

      // Full state replacement (from WebSocket broadcast)
      replaceState: (newState) => {
        if (packagesErrorTimeout) {
          clearTimeout(packagesErrorTimeout);
          packagesErrorTimeout = null;
        }
        if (bomErrorTimeout) {
          clearTimeout(bomErrorTimeout);
          bomErrorTimeout = null;
        }
        if (variablesErrorTimeout) {
          clearTimeout(variablesErrorTimeout);
          variablesErrorTimeout = null;
        }
        if (packageDetailsErrorTimeout) {
          clearTimeout(packageDetailsErrorTimeout);
          packageDetailsErrorTimeout = null;
        }
        if (projectsErrorTimeout) {
          clearTimeout(projectsErrorTimeout);
          projectsErrorTimeout = null;
        }
        if (atopileErrorTimeout) {
          clearTimeout(atopileErrorTimeout);
          atopileErrorTimeout = null;
        }

        set((state) => ({
          ...state,
          ...newState,
          isConnected: true,
        }));

        if (newState.packagesError) {
          const error = newState.packagesError;
          packagesErrorTimeout = setTimeout(() => {
            set((state) =>
              state.packagesError === error ? { packagesError: null } : {}
            );
            packagesErrorTimeout = null;
          }, ERROR_TIMEOUT_MS);
        }
        if (newState.bomError) {
          const error = newState.bomError;
          bomErrorTimeout = setTimeout(() => {
            set((state) =>
              state.bomError === error ? { bomError: null } : {}
            );
            bomErrorTimeout = null;
          }, ERROR_TIMEOUT_MS);
        }
        if (newState.variablesError) {
          const error = newState.variablesError;
          variablesErrorTimeout = setTimeout(() => {
            set((state) =>
              state.variablesError === error ? { variablesError: null } : {}
            );
            variablesErrorTimeout = null;
          }, ERROR_TIMEOUT_MS);
        }
        if (newState.packageDetailsError) {
          const error = newState.packageDetailsError;
          packageDetailsErrorTimeout = setTimeout(() => {
            set((state) =>
              state.packageDetailsError === error
                ? { packageDetailsError: null }
                : {}
            );
            packageDetailsErrorTimeout = null;
          }, ERROR_TIMEOUT_MS);
        }
        if (newState.projectsError) {
          const error = newState.projectsError;
          projectsErrorTimeout = setTimeout(() => {
            set((state) =>
              state.projectsError === error ? { projectsError: null } : {}
            );
            projectsErrorTimeout = null;
          }, ERROR_TIMEOUT_MS);
        }
        if (newState.atopile?.error) {
          const error = newState.atopile.error;
          atopileErrorTimeout = setTimeout(() => {
            set((state) =>
              state.atopile.error === error
                ? { atopile: { ...state.atopile, error: null } }
                : {}
            );
            atopileErrorTimeout = null;
          }, ERROR_TIMEOUT_MS);
        }
      },

      // Projects
      setProjects: (projects) => set((state) => {
        // Clear migrating state for projects that no longer need migration
        const stillMigrating = state.migratingProjectRoots.filter((root) => {
          const project = projects.find((p) => p.root === root);
          // Keep in migrating list if project still needs migration
          return project?.needsMigration === true;
        });

        // Validate current selection against discovered projects
        let selectedProjectRoot = state.selectedProjectRoot;
        let selectedProjectName = state.selectedProjectName;
        let selectedTargetNames = state.selectedTargetNames;

        const selectedProject = selectedProjectRoot
          ? projects.find((p) => p.root === selectedProjectRoot)
          : null;

        if (selectedProject) {
          // Selection is still valid — update name in case it changed
          selectedProjectName = selectedProject.name;
        } else if (projects.length > 0) {
          // No valid selection — auto-select first project + its first target
          const first = projects[0];
          selectedProjectRoot = first.root;
          selectedProjectName = first.name;
          selectedTargetNames = first.targets?.[0]?.name ? [first.targets[0].name] : [];
        } else {
          selectedProjectRoot = null;
          selectedProjectName = null;
          selectedTargetNames = [];
        }

        if (!_readySignalled && projects.length > 0) {
          _readySignalled = true;
          postMessage({ type: 'webviewReady' });
        }

        return {
          projects,
          isLoadingProjects: false,
          migratingProjectRoots: stillMigrating,
          selectedProjectRoot,
          selectedProjectName,
          selectedTargetNames,
        };
      }),

      setLoadingProjects: (loading) => set({ isLoadingProjects: loading }),

      setProjectsError: (error) => {
        if (projectsErrorTimeout) {
          clearTimeout(projectsErrorTimeout);
          projectsErrorTimeout = null;
        }
        set(
          error
            ? { projectsError: error, isLoadingProjects: false }
            : { projectsError: null }
        );
        if (error) {
          projectsErrorTimeout = setTimeout(() => {
            set((state) =>
              state.projectsError === error ? { projectsError: null } : {}
            );
            projectsErrorTimeout = null;
          }, ERROR_TIMEOUT_MS);
        }
      },

      selectProject: (projectRoot) => set((state) => ({
        selectedProjectRoot: projectRoot,
        selectedProjectName: projectRoot
          ? state.projects.find((p) => p.root === projectRoot)?.name ?? state.selectedProjectName
          : null,
      })),

      setSelectedTargets: (targetNames) =>
        set({ selectedTargetNames: targetNames }),

      toggleTarget: (targetName) =>
        set((state) => {
          const selected = state.selectedTargetNames;
          if (selected.includes(targetName)) {
            return {
              selectedTargetNames: selected.filter((t) => t !== targetName),
            };
          }
          return {
            selectedTargetNames: [...selected, targetName],
          };
        }),

      toggleTargetExpanded: (targetName) =>
        set((state) => {
          const expanded = state.expandedTargets;
          if (expanded.includes(targetName)) {
            return {
              expandedTargets: expanded.filter((t) => t !== targetName),
            };
          }
          return {
            expandedTargets: [...expanded, targetName],
          };
        }),

      // Migration
      addMigratingProject: (projectRoot) =>
        set((state) => ({
          migratingProjectRoots: state.migratingProjectRoots.includes(projectRoot)
            ? state.migratingProjectRoots
            : [...state.migratingProjectRoots, projectRoot],
        })),

      removeMigratingProject: (projectRoot) =>
        set((state) => ({
          migratingProjectRoots: state.migratingProjectRoots.filter((r) => r !== projectRoot),
          migrationErrors: Object.fromEntries(
            Object.entries(state.migrationErrors).filter(([k]) => k !== projectRoot)
          ),
        })),

      setMigrationError: (projectRoot, error) =>
        set((state) => {
          // Remove from migrating list
          const newMigratingRoots = state.migratingProjectRoots.filter((r) => r !== projectRoot);
          // Update error state
          const newErrors = { ...state.migrationErrors };
          if (error) {
            newErrors[projectRoot] = error;
          } else {
            delete newErrors[projectRoot];
          }
          return {
            migratingProjectRoots: newMigratingRoots,
            migrationErrors: newErrors,
          };
        }),

  // Builds
  setBuilds: (builds) => set({ builds }),

  setQueuedBuilds: (queuedBuilds) => set({ queuedBuilds }),

  setBuildHistory: (buildHistory) => set({ buildHistory }),

      selectBuild: (buildName) => set({ selectedBuildName: buildName }),

      selectBuildById: (buildId, buildName = null) =>
        set({ selectedBuildId: buildId, selectedBuildName: buildName }),

      // Log viewer
      setLogViewerBuildId: (buildId) => set({ logViewerBuildId: buildId }),

      // Active editor file - just store what's provided, backend handles lastAtoFile
      setActiveEditorFile: (filePath) => set({ activeEditorFile: filePath }),

      setLastAtoFile: (filePath) => set({ lastAtoFile: filePath }),

      // Packages
      setPackages: (packages) => set({ packages, isLoadingPackages: false }),

      updatePackageDownloads: (downloads) =>
        set((state) => ({
          packages: state.packages.map((pkg) =>
            downloads[pkg.identifier] !== undefined
              ? { ...pkg, downloads: downloads[pkg.identifier] }
              : pkg
          ),
        })),

      setLoadingPackages: (loading) => set({ isLoadingPackages: loading }),

      setPackagesError: (error) => {
        if (packagesErrorTimeout) {
          clearTimeout(packagesErrorTimeout);
          packagesErrorTimeout = null;
        }
        set({ packagesError: error, isLoadingPackages: false });
        if (error) {
          packagesErrorTimeout = setTimeout(() => {
            set((state) =>
              state.packagesError === error ? { packagesError: null } : {}
            );
            packagesErrorTimeout = null;
          }, ERROR_TIMEOUT_MS);
        }
      },

      setPackageDetails: (details) =>
        set({
          selectedPackageDetails: details,
          isLoadingPackageDetails: false,
        }),

      clearPackageDetailsAndStartLoading: () =>
        set({
          selectedPackageDetails: null,
          isLoadingPackageDetails: true,
          packageDetailsError: null,
        }),

      addInstallingPackage: (packageId) => {
        if (installErrorTimeout) {
          clearTimeout(installErrorTimeout);
          installErrorTimeout = null;
        }
        set((state) => ({
          installingPackageIds: state.installingPackageIds.includes(packageId)
            ? state.installingPackageIds
            : [...state.installingPackageIds, packageId],
          installError: null,
        }));
      },

      removeInstallingPackage: (packageId) =>
        set((state) => ({
          installingPackageIds: state.installingPackageIds.filter((id) => id !== packageId),
        })),

      clearInstallingPackages: () =>
        set({ installingPackageIds: [], installError: null }),

      setInstallError: (packageId, error) => {
        if (installErrorTimeout) {
          clearTimeout(installErrorTimeout);
          installErrorTimeout = null;
        }
        set((state) => ({
          installError: error,
          installingPackageIds: state.installingPackageIds.filter((id) => id !== packageId),
        }));
        if (error) {
          installErrorTimeout = setTimeout(() => {
            set({ installError: null });
            installErrorTimeout = null;
          }, ERROR_TIMEOUT_MS);
        }
      },

      addUpdatingDependency: (projectRoot, dependencyId) =>
        set((state) => {
          const key = `${projectRoot}:${dependencyId}`;
          return {
            updatingDependencyIds: state.updatingDependencyIds.includes(key)
              ? state.updatingDependencyIds
              : [...state.updatingDependencyIds, key],
          };
        }),

      removeUpdatingDependency: (projectRoot, dependencyId) =>
        set((state) => {
          const key = `${projectRoot}:${dependencyId}`;
          return {
            updatingDependencyIds: state.updatingDependencyIds.filter((id) => id !== key),
          };
        }),

      // Problems
      setProblems: (problems) => set({ problems, isLoadingProblems: false }),
      setLoadingProblems: (loading) => set({ isLoadingProblems: loading }),
      setDeveloperMode: (enabled) => set({ developerMode: enabled }),

      // Standard Library
      setStdlibItems: (items) =>
        set({ stdlibItems: items, isLoadingStdlib: false }),
      setLoadingStdlib: (loading) => set({ isLoadingStdlib: loading }),

      // BOM
      setBomData: (data) => set({ bomData: data, isLoadingBom: false, bomError: null }),

      setLoadingBom: (loading) => set({ isLoadingBom: loading }),
      setBomError: (error) => {
        if (bomErrorTimeout) {
          clearTimeout(bomErrorTimeout);
          bomErrorTimeout = null;
        }
        set({ bomError: error, isLoadingBom: false });
        if (error) {
          bomErrorTimeout = setTimeout(() => {
            set((state) =>
              state.bomError === error ? { bomError: null } : {}
            );
            bomErrorTimeout = null;
          }, ERROR_TIMEOUT_MS);
        }
      },

      // Variables
      setVariablesData: (data) =>
        set({ currentVariablesData: data, isLoadingVariables: false, variablesError: null }),

      setLoadingVariables: (loading) => set({ isLoadingVariables: loading }),
      setVariablesError: (error) => {
        if (variablesErrorTimeout) {
          clearTimeout(variablesErrorTimeout);
          variablesErrorTimeout = null;
        }
        set({ variablesError: error, isLoadingVariables: false });
        if (error) {
          variablesErrorTimeout = setTimeout(() => {
            set((state) =>
              state.variablesError === error ? { variablesError: null } : {}
            );
            variablesErrorTimeout = null;
          }, ERROR_TIMEOUT_MS);
        }
      },

      // Atopile config
      setAtopileConfig: (update) => {
        console.log('[Store] setAtopileConfig update:', update);
        set((state) => {
          const newAtopile = {
            ...state.atopile,
            ...update,
          };
          console.log('[Store] setAtopileConfig new state:', {
            actualBinaryPath: newAtopile.actualBinaryPath,
            localPath: newAtopile.localPath,
            source: newAtopile.source,
            actualVersion: newAtopile.actualVersion,
            fromBranch: newAtopile.fromBranch,
          });
          return { atopile: newAtopile };
        });
      },

      // Project data
      setProjectModules: (projectRoot, modules) =>
        set((state) => ({
          projectModules: {
            ...state.projectModules,
            [projectRoot]: modules,
          },
          isLoadingModules: false,
        })),

      setProjectFiles: (projectRoot, files) =>
        set((state) => ({
          projectFiles: {
            ...state.projectFiles,
            [projectRoot]: files,
          },
          isLoadingFiles: false,
        })),

      setProjectDependencies: (projectRoot, deps) =>
        set((state) => ({
          projectDependencies: {
            ...state.projectDependencies,
            [projectRoot]: deps,
          },
          isLoadingDependencies: false,
        })),

      setProjectBuilds: (projectRoot, builds) =>
        set((state) => ({
          projectBuilds: {
            ...state.projectBuilds,
            [projectRoot]: builds,
          },
          isLoadingBuilds: false,
        })),

      setLoadingModules: (loading) => set({ isLoadingModules: loading }),
      setLoadingFiles: (loading) => set({ isLoadingFiles: loading }),
      setLoadingDependencies: (loading) => set({ isLoadingDependencies: loading }),

      // Test Explorer
      setCollectedTests: (tests) =>
        set({ collectedTests: tests, isLoadingTests: false, testsError: null }),

      setLoadingTests: (loading) => set({ isLoadingTests: loading }),

      setTestsError: (error) =>
        set({ testsError: error, isLoadingTests: false }),

      setTestCollectionErrors: (errors) => set({ testCollectionErrors: errors }),

      setTestFilter: (filter) => set({ testFilter: filter }),

      setTestPaths: (paths) => set({ testPaths: paths }),

      setTestMarkers: (markers) => set({ testMarkers: markers }),

      toggleTestSelected: (nodeId) =>
        set((state) => {
          const selected = state.selectedTestNodeIds;
          if (selected.includes(nodeId)) {
            return { selectedTestNodeIds: selected.filter((id) => id !== nodeId) };
          }
          return { selectedTestNodeIds: [...selected, nodeId] };
        }),

      selectAllTests: () =>
        set((state) => ({
          selectedTestNodeIds: state.collectedTests.map((t) => t.node_id),
        })),

      clearTestSelection: () => set({ selectedTestNodeIds: [] }),

      startTestRun: (testRunId) =>
        set({ testRun: { testRunId, isRunning: true } }),

      completeTestRun: () =>
        set((state) => ({ testRun: { ...state.testRun, isRunning: false } })),

      // Manufacturing Wizard
      openManufacturingWizard: (projectRoot, targets) =>
        set({
          manufacturingWizard: {
            isOpen: true,
            currentStep: 1,
            selectedBuilds: targets.map((targetName) => ({
              projectRoot,
              targetName,
              status: 'pending',
              buildId: null,
              error: null,
            })),
            hasUncommittedChanges: false,
            uncommittedWarningDismissed: false,
            changedFiles: [],
            exportDirectory: '',
            selectedFileTypes: [...DEFAULT_FILE_TYPES],
            costEstimate: null,
            quantity: 1,
            isLoadingGitStatus: false,
            isLoadingCost: false,
            isExporting: false,
            exportError: null,
          },
        }),

      closeManufacturingWizard: () => set({ manufacturingWizard: null }),

      setManufacturingStep: (step) =>
        set((state): Partial<Store> => {
          if (!state.manufacturingWizard) return {};
          return {
            manufacturingWizard: {
              ...state.manufacturingWizard,
              currentStep: step,
            },
          };
        }),

      toggleManufacturingBuild: (targetName) =>
        set((state): Partial<Store> => {
          if (!state.manufacturingWizard) return {};
          const builds = state.manufacturingWizard.selectedBuilds;
          const exists = builds.some((b: ManufacturingBuild) => b.targetName === targetName);
          if (exists) {
            return {
              manufacturingWizard: {
                ...state.manufacturingWizard,
                selectedBuilds: builds.filter((b: ManufacturingBuild) => b.targetName !== targetName),
              },
            };
          }
          const projectRoot = builds[0]?.projectRoot || '';
          return {
            manufacturingWizard: {
              ...state.manufacturingWizard,
              selectedBuilds: [
                ...builds,
                {
                  projectRoot,
                  targetName,
                  status: 'pending' as const,
                  buildId: null,
                  error: null,
                },
              ],
            },
          };
        }),

      updateManufacturingBuild: (targetName, update) =>
        set((state): Partial<Store> => {
          if (!state.manufacturingWizard) return {};
          return {
            manufacturingWizard: {
              ...state.manufacturingWizard,
              selectedBuilds: state.manufacturingWizard.selectedBuilds.map((b: ManufacturingBuild) =>
                b.targetName === targetName ? { ...b, ...update } : b
              ),
            },
          };
        }),

      setManufacturingGitStatus: (hasChanges, files) =>
        set((state): Partial<Store> => {
          if (!state.manufacturingWizard) return {};
          return {
            manufacturingWizard: {
              ...state.manufacturingWizard,
              hasUncommittedChanges: hasChanges,
              changedFiles: files,
              isLoadingGitStatus: false,
            },
          };
        }),

      dismissUncommittedWarning: () =>
        set((state): Partial<Store> => {
          if (!state.manufacturingWizard) return {};
          return {
            manufacturingWizard: {
              ...state.manufacturingWizard,
              uncommittedWarningDismissed: true,
            },
          };
        }),

      setManufacturingExportDirectory: (directory) =>
        set((state): Partial<Store> => {
          if (!state.manufacturingWizard) return {};
          return {
            manufacturingWizard: {
              ...state.manufacturingWizard,
              exportDirectory: directory,
            },
          };
        }),

      toggleManufacturingFileType: (fileType) =>
        set((state): Partial<Store> => {
          if (!state.manufacturingWizard) return {};
          const types = state.manufacturingWizard.selectedFileTypes;
          const exists = types.includes(fileType);
          return {
            manufacturingWizard: {
              ...state.manufacturingWizard,
              selectedFileTypes: exists
                ? types.filter((t: FileExportType) => t !== fileType)
                : [...types, fileType],
            },
          };
        }),

      setManufacturingCostEstimate: (estimate) =>
        set((state): Partial<Store> => {
          if (!state.manufacturingWizard) return {};
          return {
            manufacturingWizard: {
              ...state.manufacturingWizard,
              costEstimate: estimate,
              isLoadingCost: false,
            },
          };
        }),

      setManufacturingQuantity: (quantity) =>
        set((state): Partial<Store> => {
          if (!state.manufacturingWizard) return {};
          return {
            manufacturingWizard: {
              ...state.manufacturingWizard,
              quantity: Math.max(1, quantity),
            },
          };
        }),

      setManufacturingLoading: (key, loading) =>
        set((state): Partial<Store> => {
          if (!state.manufacturingWizard) return {};
          const loadingKey =
            key === 'gitStatus'
              ? 'isLoadingGitStatus'
              : key === 'cost'
              ? 'isLoadingCost'
              : 'isExporting';
          return {
            manufacturingWizard: {
              ...state.manufacturingWizard,
              [loadingKey]: loading,
            },
          };
        }),

      setManufacturingExportError: (error) =>
        set((state): Partial<Store> => {
          if (!state.manufacturingWizard) return {};
          return {
            manufacturingWizard: {
              ...state.manufacturingWizard,
              exportError: error,
              isExporting: false,
            },
          };
        }),

      // Reset
      reset: () => set(initialState),
      }),
      { name: 'atopile-store' }
    )
  )
);

// Receive cross-webview state updates
_channel?.addEventListener('message', (event: MessageEvent) => {
  const { key, value } = event.data ?? {};
  if (key) {
    useStore.setState({ [key]: value });
  }
});

useStore.subscribe(
  (state) => ({
    projectRoot: state.selectedProjectRoot,
    projectName: state.selectedProjectName,
    targetNames: state.selectedTargetNames,
  }),
  (current, previous) => {
    const selectionChanged =
      current.projectRoot !== previous.projectRoot ||
      !arraysEqual(current.targetNames, previous.targetNames);

    if (selectionChanged) {
      postMessage({
        type: 'selectionChanged',
        projectRoot: current.projectRoot,
        targetNames: current.targetNames,
      });
    }

    // Persist for next webview load — also fires when project name is
    // resolved after discovery, even if root/targets didn't change.
    if (selectionChanged || current.projectName !== previous.projectName) {
      const api = getVsCodeApi();
      api?.setState({
        selectedProjectRoot: current.projectRoot,
        selectedProjectName: current.projectName,
        selectedTargetNames: current.targetNames,
      });
    }
  }
);
// Selectors for common derived state
export const useSelectedProject = () =>
  useStore((state) => {
    if (!state.selectedProjectRoot) return null;
    return state.projects.find((p) => p.root === state.selectedProjectRoot);
  });

export const useSelectedBuild = () =>
  useStore((state) => {
    // Prefer buildId if available
    if (state.selectedBuildId) {
      return state.builds.find((b) => b.buildId === state.selectedBuildId);
    }
    // Fall back to buildName for backwards compatibility
    if (!state.selectedBuildName) return null;
    return state.builds.find(
      (b) => b.displayName === state.selectedBuildName || b.name === state.selectedBuildName
    );
  });

export const useFilteredProblems = () =>
  useStore((state) => {
    const { problems, problemFilter } = state;
    return problems.filter((p) => {
      if (!problemFilter.levels.includes(p.level)) return false;
      if (
        problemFilter.buildNames.length > 0 &&
        p.buildName &&
        !problemFilter.buildNames.includes(p.buildName)
      )
        return false;
      if (
        problemFilter.stageIds.length > 0 &&
        p.stage &&
        !problemFilter.stageIds.includes(p.stage)
      )
        return false;
      return true;
    });
  });
