/// Vault tab entry point — full "desktop-like" sidebar + pane layout.
///
/// Registers as the shared state host so popup components (item-detail,
/// item-form, trash, devices, settings, etc.) render natively in the
/// vault tab's pane area.

import type { Request, Response } from '../shared/messages';
import { registerHost, sendMessage } from '../shared/state';
import { encodeBinary, decodeBinary } from '../shared/message-binary';
import { type ErrorCta } from '../shared/error-copy';
import {
  type VaultController, type VaultState, type VaultView,
  escapeHtml,
} from './vault-context';
import {
  render, applyShellViewClass,
  openTypePanel, closeTypePanel, applyVaultColorScheme,
  wireSessionExpiredListener,
} from './vault-shell';
import { wireSidebar, renderSidebarCategories } from './vault-sidebar';
import { renderListPane } from './vault-list';
import {
  openDrawer, closeDrawer, renderDrawer, selectItemForDrawer,
} from './vault-drawer';
import { parseHash, setHash, renderPane, loadManifest, selectItem } from './vault-router';

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

// Plain transport to the service worker, registered as the host's sendMessage.
// The shared sendMessage() wrapper (shared/state.ts) layers the session-lost
// → lock-screen intercept on top of this for every UI RPC.
function postToServiceWorker(request: Request): Promise<Response> {
  return new Promise((resolve) => {
    // Binary payloads (backups, imports, attachments) ride as base64 envelopes
    // over the channel and are decoded back here. See message-binary.ts.
    chrome.runtime.sendMessage(encodeBinary(request), (raw: Response) => resolve(decodeBinary(raw)));
  });
}

// ---------------------------------------------------------------------------
// State
// ---------------------------------------------------------------------------

const state: VaultState = {
  unlocked: false,
  view: 'list',
  entries: [],
  selectedId: null,
  selectedItem: null,
  selectedIndex: 0,
  searchQuery: '',
  activeGroup: null,
  drawerOpen: false,
  typePanelOpen: false,
  vaultSettings: null,
  generatorDefaults: null,
  error: null,
  loading: false,
  newType: null,
  capturedTabId: null,
  capturedUrl: '',
  historyItemId: null,
};

// ---------------------------------------------------------------------------
// Controller — carries state + cross-module re-render hooks
// ---------------------------------------------------------------------------

const ctx: VaultController = {
  state,
  sendMessage,
  render: () => render(ctx),
  renderPane: () => renderPane(ctx),
  renderListPane: () => renderListPane(ctx),
  renderSidebarCategories: () => renderSidebarCategories(ctx),
  renderDrawer: (item) => renderDrawer(ctx, item),
  applyShellViewClass: () => applyShellViewClass(ctx),
  setHash,
  openDrawer: () => openDrawer(),
  closeDrawer: () => closeDrawer(ctx),
  selectItemForDrawer: (id) => selectItemForDrawer(ctx, id),
  openTypePanel: () => openTypePanel(ctx),
  closeTypePanel: () => closeTypePanel(ctx),
  wireSidebar: () => wireSidebar(ctx),
  loadManifest: () => loadManifest(ctx),
};

// ---------------------------------------------------------------------------
// Register as shared state host
// ---------------------------------------------------------------------------

registerHost({
  getState: () => state,
  setState: (partial) => {
    Object.assign(state, partial);
    renderPane(ctx);
  },
  navigate: (view, extras) => {
    if (view === 'locked') {
      // Session lost (SW evicted mid-session). The vault shows its lock
      // screen off state.unlocked, so flip it and drop the in-memory data.
      state.unlocked = false;
      state.selectedId = null;
      state.selectedItem = null;
      state.entries = [];
      state.error = (extras?.error as string | undefined) ?? null;
      render(ctx);
      return;
    }
    Object.assign(state, { view, error: null, loading: false, ...extras });
    setHash(view as VaultView);
    applyShellViewClass(ctx);
    renderSidebarCategories(ctx);
    if (state.view === 'list') renderListPane(ctx);
    renderPane(ctx);
  },
  sendMessage: postToServiceWorker,
  escapeHtml,
  popOutToTab: () => {},
  isInTab: () => true,
  openVaultTab: () => {},
});

// ---------------------------------------------------------------------------
// Init
// ---------------------------------------------------------------------------

document.addEventListener('DOMContentLoaded', async () => {
  await applyVaultColorScheme();

  // Delegated handler for .error-cta buttons — set up once on the stable root.
  const app = document.getElementById('vault-app')!;
  app.addEventListener('click', (e) => {
    const btn = (e.target as HTMLElement).closest<HTMLButtonElement>('.error-cta');
    if (!btn) return;
    const cta = btn.dataset.cta as ErrorCta['action'];
    switch (cta) {
      case 'unlock': {
        document.getElementById('vault-passphrase')?.focus();
        break;
      }
      case 'open_setup': {
        void chrome.tabs.create({ url: chrome.runtime.getURL('setup.html') });
        break;
      }
      case 'reload_extension': {
        chrome.runtime.reload();
        break;
      }
    }
  });

  // Check if already unlocked
  const resp = await sendMessage({ type: 'is_unlocked' });
  if (resp.ok) {
    const data = resp.data as { unlocked: boolean };
    if (data.unlocked) {
      state.unlocked = true;
      await loadManifest(ctx);
    }
  }

  render(ctx);

  wireSessionExpiredListener(ctx);

  // Hash change listener
  window.addEventListener('hashchange', () => {
    if (!state.unlocked) return;

    const route = parseHash();
    state.view = route.view;
    applyShellViewClass(ctx);

    // If navigating to a detail/edit view for an item we already have loaded
    if ((route.view === 'detail' || route.view === 'edit') && route.id) {
      if (state.selectedId === route.id && state.selectedItem) {
        renderPane(ctx);
        renderSidebarCategories(ctx);
        if (state.view === 'list') renderListPane(ctx);
        return;
      }
      // Need to fetch the item
      selectItem(ctx, route.id);
      return;
    }

    // For non-item views, just re-render the pane
    state.selectedId = null;
    state.selectedItem = null;
    renderSidebarCategories(ctx);
    if (state.view === 'list') renderListPane(ctx);
    renderPane(ctx);
  });
});
