<script setup lang="ts">
import { ref, onMounted, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';

import {
  BaseWallet,
  Bytes,
  type Address,
} from '@xocash/stack';

import {
  type WalletHDWatch,
  type WalletP2PKHWatch,
} from '../xo-extensions/index.js';

import { binToHex } from '@bitauth/libauth';

import { useApp } from '../services/app.js';

//-----------------------------------------------------------------------------
// Types
//-----------------------------------------------------------------------------

type WalletType = 'p2pkh' | 'hd';

type AddressInfo = {
  address: Address;
  chainPath: number;
  index: number;
  cashAddr: string;
  derivationPath: string;
  lockscriptHex: string;
  txCount: number;
  utxoCount: number;
};

//-----------------------------------------------------------------------------
// App & Router
//-----------------------------------------------------------------------------

const app = useApp();
const route = useRoute();
const router = useRouter();

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

/** The wallet key from the route param. */
const key = computed(() => route.params.key as string);

/** The account path from query params (for master xpubs). */
const accountPath = computed(() => route.query.accountPath as string | undefined);

/** The detected wallet type. */
const walletType = computed<WalletType | null>(() => {
  if (isXPub(key.value)) return 'hd';
  if (isPublicKey(key.value)) return 'p2pkh';
  return null;
});

/** Loading state. */
const isLoading = ref(true);

/** Error message if wallet creation fails. */
const error = ref<string | null>(null);

/** Filter for chain path (0 = external, 1 = internal, null = all). */
const chainPathFilter = ref<number | null>(null);

/** Search query for filtering addresses. */
const searchQuery = ref('');

//-----------------------------------------------------------------------------
// Computed Wallet
//-----------------------------------------------------------------------------

/** The genesis data used to derive the wallet ID. */
const genesisData = computed(() => {
  return walletType.value === 'hd'
    ? {
        type: 'WalletHDWatch',
        xpub: key.value,
        derivationPath: accountPath.value ?? '',
      } as const
    : {
        type: 'WalletP2PKHWatch',
        publicKey: key.value,
      } as const;
});

/** The wallet instance from the app's wallet manager. */
const wallet = computed(() => {
  const walletId = BaseWallet.deriveId(genesisData.value);
  return app.wallets.wallets[Bytes.from(walletId).toHex()];
});

//-----------------------------------------------------------------------------
// Wallet Type Detection
//-----------------------------------------------------------------------------

/**
 * Determines if a key is an xpub (extended public key).
 *
 * @param key - The key to check.
 * @returns True if the key is an xpub.
 */
function isXPub(key: string): boolean {
  return key.startsWith('xpub') || key.startsWith('tpub');
}

/**
 * Determines if a key is a valid compressed public key.
 *
 * @param key - The key to check.
 * @returns True if the key is a valid public key.
 */
function isPublicKey(key: string): boolean {
  if (key.length !== 66) return false;
  if (!key.startsWith('02') && !key.startsWith('03')) return false;
  return /^[0-9a-fA-F]+$/.test(key);
}

//-----------------------------------------------------------------------------
// Wallet Initialization
//-----------------------------------------------------------------------------

/**
 * Creates and initializes the wallet if it doesn't exist.
 */
async function initializeWallet() {
  isLoading.value = true;
  error.value = null;

  try {
    if (!walletType.value) {
      throw new Error(
        'Invalid key format. Please provide a valid public key or xpub.'
      );
    }

    // Create the wallet via the app's wallet manager.
    await app.wallets.createWallet(genesisData.value);
  } catch (err) {
    error.value = err instanceof Error ? err.message : 'Failed to initialize wallet';
    console.error('Wallet initialization error:', err);
  } finally {
    isLoading.value = false;
  }
}

//-----------------------------------------------------------------------------
// Lifecycle
//-----------------------------------------------------------------------------

onMounted(() => {
  initializeWallet();
});

//-----------------------------------------------------------------------------
// Computed Values
//-----------------------------------------------------------------------------

/**
 * Map of transaction counts per address lockscript.
 * Key: lockscript hex, Value: number of transactions involving this address.
 */
const txCountsByLockscript = computed<Map<string, number>>(() => {
  if (!wallet.value) return new Map();

  const counts = new Map<string, number>();

  // Iterate through all transactions in the wallet.
  wallet.value.transactions.value.forEach((tx) => {
    const seenLockscripts = new Set<string>();

    // Check outputs for addresses belonging to this wallet.
    tx.transaction.getOutputs().forEach((output) => {
      const lockscriptHex = binToHex(output.lockingBytecode);
      // Only count once per transaction.
      if (!seenLockscripts.has(lockscriptHex)) {
        seenLockscripts.add(lockscriptHex);
        counts.set(lockscriptHex, (counts.get(lockscriptHex) ?? 0) + 1);
      }
    });

    // Check inputs (source outputs) for addresses belonging to this wallet.
    tx.sourceOutputs.forEach((sourceOutput) => {
      const lockscriptHex = binToHex(sourceOutput.lockingBytecode);
      // Only count once per transaction.
      if (!seenLockscripts.has(lockscriptHex)) {
        seenLockscripts.add(lockscriptHex);
        counts.set(lockscriptHex, (counts.get(lockscriptHex) ?? 0) + 1);
      }
    });
  });

  return counts;
});

/**
 * Map of UTXO counts per address lockscript.
 * Key: lockscript hex, Value: number of unspent outputs for this address.
 */
const utxoCountsByLockscript = computed<Map<string, number>>(() => {
  if (!wallet.value) return new Map();

  const counts = new Map<string, number>();

  // Iterate through all UTXOs in the wallet.
  wallet.value.unspents.value.forEach((blockchainUtxo) => {
    const lockscriptHex = binToHex(blockchainUtxo.utxo.output.lockingBytecode);
    counts.set(lockscriptHex, (counts.get(lockscriptHex) ?? 0) + 1);
  });

  return counts;
});

/** Derived addresses with metadata built from the wallet. */
const addressList = computed<AddressInfo[]>(() => {
  if (!wallet.value) return [];

  const addresses: AddressInfo[] = [];
  const baseWallet = wallet.value.wallet;

  if (walletType.value === 'hd' && 'wallets' in baseWallet) {
    // HD Wallet - iterate through child wallets.
    const hdWallet = baseWallet as WalletHDWatch;
    const basePath = hdWallet.genesisData.derivationPath;

    for (const [chainPath, childWallets] of Object.entries(hdWallet.wallets)) {
      childWallets.forEach((childWallet, index) => {
        const address = childWallet.publicKey.deriveAddress();
        const chainPathNum = Number(chainPath);
        const lockscriptHex = address.toLockscriptHex();
        addresses.push({
          address,
          chainPath: chainPathNum,
          index,
          cashAddr: address.toCashAddr(),
          derivationPath: `${basePath}/${chainPathNum}/${index}`,
          lockscriptHex,
          txCount: txCountsByLockscript.value.get(lockscriptHex) ?? 0,
          utxoCount: utxoCountsByLockscript.value.get(lockscriptHex) ?? 0,
        });
      });
    }
  } else {
    // P2PKH Wallet - single address.
    const p2pkhWallet = baseWallet as WalletP2PKHWatch;
    const address = p2pkhWallet.publicKey.deriveAddress();
    const lockscriptHex = address.toLockscriptHex();
    addresses.push({
      address,
      chainPath: 0,
      index: 0,
      cashAddr: address.toCashAddr(),
      derivationPath: '—',
      lockscriptHex,
      txCount: txCountsByLockscript.value.get(lockscriptHex) ?? 0,
      utxoCount: utxoCountsByLockscript.value.get(lockscriptHex) ?? 0,
    });
  }

  return addresses;
});

/** Filtered addresses based on chain path and search query. */
const filteredAddresses = computed(() => {
  let result = addressList.value;

  // Filter by chain path.
  if (chainPathFilter.value !== null) {
    result = result.filter((a) => a.chainPath === chainPathFilter.value);
  }

  // Filter by search query.
  if (searchQuery.value) {
    const query = searchQuery.value.toLowerCase();
    result = result.filter((a) => a.cashAddr.toLowerCase().includes(query));
  }

  return result;
});

/** Count of external addresses. */
const externalCount = computed(() =>
  addressList.value.filter((a) => a.chainPath === 0).length
);

/** Count of internal (change) addresses. */
const internalCount = computed(() =>
  addressList.value.filter((a) => a.chainPath === 1).length
);

//-----------------------------------------------------------------------------
// Actions
//-----------------------------------------------------------------------------

function goBack() {
  router.push({
    name: 'wallet',
    params: { key: key.value },
    query: accountPath.value ? { accountPath: accountPath.value } : {},
  });
}

function copyAddress(cashAddr: string) {
  navigator.clipboard.writeText(cashAddr);
}

function getChainLabel(chainPath: number): string {
  return chainPath === 0 ? 'External' : 'Change';
}

function setFilter(filter: number | null) {
  chainPathFilter.value = filter;
}
</script>

<template>
  <div class="addresses-page">
    <!-- Header -->
    <header class="header">
      <button class="back-button" @click="goBack">
        ← Back to Wallet
      </button>
      <div class="wallet-type-badge" v-if="walletType">
        {{ walletType === 'hd' ? 'HD Wallet' : 'P2PKH Wallet' }}
      </div>
    </header>

    <!-- Loading State -->
    <div v-if="isLoading" class="loading-state">
      <div class="spinner"></div>
      <p>Loading addresses...</p>
    </div>

    <!-- Error State -->
    <div v-else-if="error" class="error-state">
      <div class="error-icon">⚠️</div>
      <h2>Error</h2>
      <p>{{ error }}</p>
      <button class="retry-button" @click="goBack">
        Go Back
      </button>
    </div>

    <!-- Addresses Content -->
    <div v-else class="addresses-content">
      <h1 class="page-title">Addresses</h1>

      <!-- Stats -->
      <div class="stats-row" v-if="walletType === 'hd'">
        <div class="stat-card">
          <div class="stat-value">{{ addressList.length }}</div>
          <div class="stat-label">Total</div>
        </div>
        <div class="stat-card">
          <div class="stat-value">{{ externalCount }}</div>
          <div class="stat-label">External</div>
        </div>
        <div class="stat-card">
          <div class="stat-value">{{ internalCount }}</div>
          <div class="stat-label">Change</div>
        </div>
      </div>

      <!-- Filters -->
      <div class="filters" v-if="walletType === 'hd'">
        <div class="filter-buttons">
          <button
            :class="['filter-btn', { active: chainPathFilter === null }]"
            @click="setFilter(null)"
          >
            All
          </button>
          <button
            :class="['filter-btn', { active: chainPathFilter === 0 }]"
            @click="setFilter(0)"
          >
            External
          </button>
          <button
            :class="['filter-btn', { active: chainPathFilter === 1 }]"
            @click="setFilter(1)"
          >
            Change
          </button>
        </div>

        <input
          v-model="searchQuery"
          type="text"
          placeholder="Search addresses..."
          class="search-input"
        />
      </div>

      <!-- Address List -->
      <div class="address-list">
        <div
          v-for="addr in filteredAddresses"
          :key="addr.cashAddr"
          class="address-item"
        >
          <div class="address-info">
            <div class="address-index">
              <span class="chain-badge" :class="addr.chainPath === 0 ? 'external' : 'internal'">
                {{ getChainLabel(addr.chainPath) }}
              </span>
              <span class="index-number">#{{ addr.index }}</span>
              <span class="derivation-path">{{ addr.derivationPath }}</span>
            </div>
            <code class="address-value">{{ addr.cashAddr }}</code>
            <div class="address-stats">
              <span class="stat-badge" :class="{ inactive: addr.txCount === 0 }">
                {{ addr.txCount }} tx{{ addr.txCount !== 1 ? 's' : '' }}
              </span>
              <span class="stat-badge" :class="{ inactive: addr.utxoCount === 0 }">
                {{ addr.utxoCount }} UTXO{{ addr.utxoCount !== 1 ? 's' : '' }}
              </span>
            </div>
          </div>
          <button class="copy-button" @click="copyAddress(addr.cashAddr)" title="Copy address">
            📋
          </button>
        </div>

        <div v-if="filteredAddresses.length === 0" class="empty-state">
          <p v-if="searchQuery">No addresses match your search.</p>
          <p v-else>No addresses found.</p>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.addresses-page {
  min-height: 100vh;
  background: linear-gradient(135deg, #0f0f1a 0%, #1a1a2e 50%, #16213e 100%);
  color: #fff;
  padding: 1.5rem;
}

/* Header */
.header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 2rem;
  max-width: 900px;
  margin-left: auto;
  margin-right: auto;
}

.back-button {
  padding: 0.5rem 1rem;
  font-size: 0.875rem;
  font-weight: 500;
  border: 1px solid #2d3748;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.05);
  color: #a0aec0;
  cursor: pointer;
  transition: all 0.2s ease;
}

.back-button:hover {
  background: rgba(255, 255, 255, 0.1);
  border-color: #4a5568;
}

.wallet-type-badge {
  padding: 0.375rem 0.75rem;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  border-radius: 9999px;
  background: linear-gradient(135deg, rgba(0, 212, 255, 0.2), rgba(123, 44, 191, 0.2));
  border: 1px solid rgba(0, 212, 255, 0.3);
  color: #00d4ff;
}

/* Loading State */
.loading-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 60vh;
  text-align: center;
}

.spinner {
  width: 48px;
  height: 48px;
  border: 3px solid #2d3748;
  border-top-color: #00d4ff;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-bottom: 1rem;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

.loading-state p {
  color: #a0aec0;
  font-size: 1.125rem;
}

/* Error State */
.error-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 60vh;
  text-align: center;
}

.error-icon {
  font-size: 3rem;
  margin-bottom: 1rem;
}

.error-state h2 {
  font-size: 1.5rem;
  margin-bottom: 0.5rem;
}

.error-state p {
  color: #fc8181;
  margin-bottom: 1.5rem;
}

.retry-button {
  padding: 0.75rem 1.5rem;
  font-size: 1rem;
  font-weight: 500;
  border: none;
  border-radius: 8px;
  background: #2d3748;
  color: #fff;
  cursor: pointer;
  transition: all 0.2s ease;
}

.retry-button:hover {
  background: #4a5568;
}

/* Content */
.addresses-content {
  max-width: 900px;
  margin: 0 auto;
}

.page-title {
  font-size: 2rem;
  font-weight: 700;
  margin-bottom: 1.5rem;
  background: linear-gradient(135deg, #00d4ff, #7b2cbf);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

/* Stats Row */
.stats-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
  margin-bottom: 1.5rem;
}

.stat-card {
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid #2d3748;
  border-radius: 12px;
  padding: 1rem;
  text-align: center;
}

.stat-value {
  font-size: 1.5rem;
  font-weight: 700;
  color: #fff;
  font-family: 'JetBrains Mono', 'Fira Code', monospace;
}

.stat-label {
  font-size: 0.75rem;
  font-weight: 500;
  color: #718096;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-top: 0.25rem;
}

/* Filters */
.filters {
  display: flex;
  gap: 1rem;
  margin-bottom: 1.5rem;
  flex-wrap: wrap;
}

.filter-buttons {
  display: flex;
  gap: 0.5rem;
}

.filter-btn {
  padding: 0.5rem 1rem;
  font-size: 0.875rem;
  font-weight: 500;
  border: 1px solid #2d3748;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.03);
  color: #a0aec0;
  cursor: pointer;
  transition: all 0.2s ease;
}

.filter-btn:hover {
  background: rgba(255, 255, 255, 0.08);
}

.filter-btn.active {
  background: linear-gradient(135deg, rgba(0, 212, 255, 0.2), rgba(123, 44, 191, 0.2));
  border-color: rgba(0, 212, 255, 0.3);
  color: #00d4ff;
}

.search-input {
  flex: 1;
  min-width: 200px;
  padding: 0.5rem 1rem;
  font-size: 0.875rem;
  border: 1px solid #2d3748;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.03);
  color: #fff;
  font-family: 'JetBrains Mono', 'Fira Code', monospace;
}

.search-input::placeholder {
  color: #4a5568;
}

.search-input:focus {
  outline: none;
  border-color: #00d4ff;
}

/* Address List */
.address-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.address-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid #2d3748;
  border-radius: 12px;
  padding: 1rem 1.25rem;
  transition: all 0.2s ease;
}

.address-item:hover {
  background: rgba(255, 255, 255, 0.05);
  border-color: #4a5568;
}

.address-info {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 0;
  flex: 1;
}

.address-index {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.chain-badge {
  padding: 0.125rem 0.5rem;
  font-size: 0.625rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  border-radius: 4px;
}

.chain-badge.external {
  background: rgba(72, 187, 120, 0.2);
  color: #48bb78;
}

.chain-badge.internal {
  background: rgba(237, 137, 54, 0.2);
  color: #ed8936;
}

.index-number {
  font-size: 0.75rem;
  color: #718096;
  font-family: 'JetBrains Mono', 'Fira Code', monospace;
}

.derivation-path {
  font-size: 0.7rem;
  color: #4a5568;
  font-family: 'JetBrains Mono', 'Fira Code', monospace;
  margin-left: 0.5rem;
  padding: 0.125rem 0.375rem;
  background: rgba(255, 255, 255, 0.03);
  border-radius: 4px;
}

.address-value {
  font-family: 'JetBrains Mono', 'Fira Code', monospace;
  font-size: 0.8rem;
  color: #a0aec0;
  word-break: break-all;
}

.address-stats {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.25rem;
}

.stat-badge {
  display: inline-flex;
  align-items: center;
  padding: 0.125rem 0.5rem;
  font-size: 0.625rem;
  font-weight: 500;
  font-family: 'JetBrains Mono', 'Fira Code', monospace;
  border-radius: 4px;
  background: rgba(0, 212, 255, 0.1);
  color: #00d4ff;
  border: 1px solid rgba(0, 212, 255, 0.2);
}

.stat-badge.inactive {
  background: rgba(255, 255, 255, 0.03);
  color: #4a5568;
  border-color: #2d3748;
}

.copy-button {
  padding: 0.5rem;
  font-size: 1rem;
  border: none;
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.05);
  cursor: pointer;
  transition: all 0.2s ease;
  flex-shrink: 0;
}

.copy-button:hover {
  background: rgba(255, 255, 255, 0.1);
  transform: scale(1.05);
}

.empty-state {
  text-align: center;
  padding: 3rem;
  color: #718096;
  background: rgba(255, 255, 255, 0.02);
  border-radius: 12px;
  border: 1px dashed #2d3748;
}
</style>

