import { finalizeEvent, type EventTemplate, nip04 } from 'nostr-tools'
import { BaseService } from '@/core/base/BaseService'
import type { Stall, Product, Order } from '@/modules/market/stores/market'

export interface NostrmarketStall {
  id: string
  name: string
  description?: string
  currency: string
  shipping: Array<{
    id: string
    name: string
    cost: number
    countries: string[]
  }>
}

export interface NostrmarketProduct {
  id: string
  stall_id: string
  name: string
  description?: string
  images: string[]
  categories: string[]
  price: number
  quantity: number
  currency: string
}

export interface NostrmarketOrder {
  id: string
  items: Array<{
    product_id: string
    quantity: number
  }>
  contact: {
    name: string
    email?: string
    phone?: string
  }
  address?: {
    street: string
    city: string
    state: string
    country: string
    postal_code: string
  }
  shipping_id: string
}

export interface NostrmarketPaymentRequest {
  type: 1
  id: string
  message?: string
  payment_options: Array<{
    type: string
    link: string
  }>
}

export interface NostrmarketOrderStatus {
  type: 2
  id: string
  message?: string
  paid?: boolean
  shipped?: boolean
}

export class NostrmarketService extends BaseService {
  // Service metadata
  protected readonly metadata = {
    name: 'NostrmarketService',
    version: '1.0.0',
    dependencies: ['RelayHub', 'AuthService']
  }

  /**
   * Service-specific initialization (called by BaseService)
   */
  protected async onInitialize(): Promise<void> {
    this.debug('NostrmarketService initialized')
    // Service doesn't need special initialization
  }

  /**
   * Check if the service is ready for Nostr operations
   */
  get isReady(): boolean {
    try {
      this.getAuth()
      return true
    } catch {
      return false
    }
  }

  /**
   * Convert hex string to Uint8Array (browser-compatible)
   */
  private hexToUint8Array(hex: string): Uint8Array {
    const bytes = new Uint8Array(hex.length / 2)
    for (let i = 0; i < hex.length; i += 2) {
      bytes[i / 2] = parseInt(hex.substr(i, 2), 16)
    }
    return bytes
  }

  private getAuth() {
    // Use injected AuthService only
    if (!this.authService?.isAuthenticated.value) {
      throw new Error('User not authenticated')
    }

    const user = this.authService.user.value
    const pubkey = user?.pubkey
    const prvkey = user?.prvkey
    
    if (!pubkey || !prvkey) {
      this.debug('Auth check failed:', {
        userExists: !!user,
        hasPubkey: !!pubkey,
        hasPrvkey: !!prvkey
      })
      throw new Error('Nostr keys not available. Please ensure your Nostr identity is configured in your profile.')
    }
    
    // Validate that we have proper hex strings
    if (!/^[0-9a-fA-F]{64}$/.test(pubkey)) {
      throw new Error(`Invalid public key format: ${pubkey.substring(0, 10)}...`)
    }
    
    if (!/^[0-9a-fA-F]{64}$/.test(prvkey)) {
      throw new Error(`Invalid private key format: ${prvkey.substring(0, 10)}...`)
    }
    
    console.log('🔑 Key debug:', {
      pubkey: pubkey.substring(0, 10) + '...',
      prvkey: prvkey.substring(0, 10) + '...',
      pubkeyIsHex: /^[0-9a-fA-F]{64}$/.test(pubkey),
      prvkeyIsHex: /^[0-9a-fA-F]{64}$/.test(prvkey),
      pubkeyLength: pubkey.length,
      prvkeyLength: prvkey.length,
      pubkeyType: typeof pubkey,
      prvkeyType: typeof prvkey,
      pubkeyIsString: typeof pubkey === 'string',
      prvkeyIsString: typeof prvkey === 'string'
    })
    
    return {
      pubkey,
      prvkey
    }
  }

  /**
   * Publish a stall event (kind 30017) to Nostr
   */
  async publishStall(stall: Stall): Promise<string> {
    const { prvkey } = this.getAuth()
    
    const stallData: NostrmarketStall = {
      id: stall.id,
      name: stall.name,
      description: stall.description,
      currency: stall.currency,
      shipping: (stall.shipping || []).map(zone => ({
        id: zone.id,
        name: zone.name,
        cost: zone.cost,
        countries: []
      }))
    }

    const eventTemplate: EventTemplate = {
      kind: 30017,
      tags: [
        ['t', 'stall'],
        ['t', 'nostrmarket']
      ],
      content: JSON.stringify(stallData),
      created_at: Math.floor(Date.now() / 1000)
    }

    const prvkeyBytes = this.hexToUint8Array(prvkey)
    const event = finalizeEvent(eventTemplate, prvkeyBytes)
    const result = await this.relayHub.publishEvent(event)
    
    console.log('Stall published to nostrmarket:', {
      stallId: stall.id,
      eventId: result,
      content: stallData
    })

    return result.success.toString()
  }

  /**
   * Publish a product event (kind 30018) to Nostr
   */
  async publishProduct(product: Product): Promise<string> {
    const { prvkey } = this.getAuth()
    
    const productData: NostrmarketProduct = {
      id: product.id,
      stall_id: product.stall_id,
      name: product.name,
      description: product.description,
      images: product.images || [],
      categories: product.categories || [],
      price: product.price,
      quantity: product.quantity,
      currency: product.currency
    }

    const eventTemplate: EventTemplate = {
      kind: 30018,
      tags: [
        ['t', 'product'],
        ['t', 'nostrmarket'],
        ['t', 'stall', product.stall_id],
        ...(product.categories || []).map(cat => ['t', cat])
      ],
      content: JSON.stringify(productData),
      created_at: Math.floor(Date.now() / 1000)
    }

    const prvkeyBytes = this.hexToUint8Array(prvkey)
    const event = finalizeEvent(eventTemplate, prvkeyBytes)
    const result = await this.relayHub.publishEvent(event)
    
    console.log('Product published to nostrmarket:', {
      productId: product.id,
      eventId: result,
      content: productData
    })

    return result.success.toString()
  }

  /**
   * Publish an order event (kind 4 encrypted DM) to nostrmarket
   */
  async publishOrder(order: Order, merchantPubkey: string): Promise<string> {
    const { prvkey } = this.getAuth()
    
    // Convert order to nostrmarket format - exactly matching the specification
    const orderData = {
      type: 0, // DirectMessageType.CUSTOMER_ORDER
      id: order.id,
      items: order.items.map(item => ({
        product_id: item.productId,
        quantity: item.quantity
      })),
      contact: {
        name: order.contactInfo?.message || order.contactInfo?.email || 'Unknown',
        email: order.contactInfo?.email || ''
        // Remove phone field - not in nostrmarket specification
      },
      // Only include address if it's a physical good and address is provided
      ...(order.shippingZone?.requiresPhysicalShipping && order.contactInfo?.address ? {
        address: order.contactInfo.address
      } : {}),
      shipping_id: order.shippingZone?.id || 'online'
    }

    // Encrypt the message using NIP-04
    console.log('🔐 NIP-04 encryption debug:', {
      prvkeyType: typeof prvkey,
      prvkeyIsString: typeof prvkey === 'string',
      prvkeyLength: prvkey.length,
      prvkeySample: prvkey.substring(0, 10) + '...',
      merchantPubkeyType: typeof merchantPubkey,
      merchantPubkeyLength: merchantPubkey.length,
      orderDataString: JSON.stringify(orderData).substring(0, 50) + '...'
    })

    let encryptedContent: string
    try {
      encryptedContent = await nip04.encrypt(prvkey, merchantPubkey, JSON.stringify(orderData))
      console.log('🔐 NIP-04 encryption successful:', {
        encryptedContentLength: encryptedContent.length,
        encryptedContentSample: encryptedContent.substring(0, 50) + '...'
      })
    } catch (error) {
      console.error('🔐 NIP-04 encryption failed:', error)
      throw error
    }

    const eventTemplate: EventTemplate = {
      kind: 4, // Encrypted DM
      tags: [['p', merchantPubkey]], // Recipient (merchant)
      content: encryptedContent, // Use encrypted content
      created_at: Math.floor(Date.now() / 1000)
    }

    console.log('🔧 finalizeEvent debug:', {
      prvkeyType: typeof prvkey,
      prvkeyIsString: typeof prvkey === 'string',
      prvkeyLength: prvkey.length,
      prvkeySample: prvkey.substring(0, 10) + '...',
      encodedPrvkeyType: typeof new TextEncoder().encode(prvkey),
      encodedPrvkeyLength: new TextEncoder().encode(prvkey).length,
      eventTemplate
    })

    // Convert hex string to Uint8Array properly
    const prvkeyBytes = this.hexToUint8Array(prvkey)
    console.log('🔧 prvkeyBytes debug:', {
      prvkeyBytesType: typeof prvkeyBytes,
      prvkeyBytesLength: prvkeyBytes.length,
      prvkeyBytesIsUint8Array: prvkeyBytes instanceof Uint8Array
    })

    const event = finalizeEvent(eventTemplate, prvkeyBytes)
    const result = await this.relayHub.publishEvent(event)
    
    console.log('Order published to nostrmarket:', {
      orderId: order.id,
      eventId: result,
      merchantPubkey,
      content: orderData,
      encryptedContent: encryptedContent.substring(0, 50) + '...'
    })

    return result.success.toString()
  }

  /**
   * Handle incoming payment request from merchant (type 1)
   */
  async handlePaymentRequest(paymentRequest: NostrmarketPaymentRequest): Promise<void> {
    console.log('Received payment request from merchant:', {
      orderId: paymentRequest.id,
      message: paymentRequest.message,
      paymentOptions: paymentRequest.payment_options
    })

    // Find the Lightning payment option
    const lightningOption = paymentRequest.payment_options.find(option => option.type === 'ln')
    if (!lightningOption) {
      console.error('No Lightning payment option found in payment request')
      return
    }

    // Update the order in the store with payment request
    const { useMarketStore } = await import('../stores/market')
    const marketStore = useMarketStore()
    
    const order = Object.values(marketStore.orders).find(o => 
      o.id === paymentRequest.id || o.originalOrderId === paymentRequest.id
    )
    
    if (order) {
      // Update order with payment request details
      const updatedOrder = {
        ...order,
        paymentRequest: lightningOption.link,
        paymentStatus: 'pending' as const,
        status: 'pending' as const, // Ensure status is pending for payment
        updatedAt: Math.floor(Date.now() / 1000),
        items: [...order.items], // Convert readonly to mutable
        shippingZone: order.shippingZone ? {
          ...order.shippingZone,
          countries: order.shippingZone.countries ? [...order.shippingZone.countries] : undefined
        } : order.shippingZone
      }
      
      // Generate QR code for the payment request
      try {
        const QRCode = await import('qrcode')
        const qrCodeDataUrl = await QRCode.toDataURL(lightningOption.link, {
          width: 256,
          margin: 2,
          color: {
            dark: '#000000',
            light: '#FFFFFF'
          }
        })
        updatedOrder.qrCodeDataUrl = qrCodeDataUrl
        updatedOrder.qrCodeLoading = false
        updatedOrder.qrCodeError = null
      } catch (error) {
        console.error('Failed to generate QR code:', error)
        updatedOrder.qrCodeError = 'Failed to generate QR code'
        updatedOrder.qrCodeLoading = false
      }
      
      marketStore.updateOrder(order.id, updatedOrder)
      
      console.log('Order updated with payment request:', {
        orderId: paymentRequest.id,
        paymentRequest: lightningOption.link.substring(0, 50) + '...',
        status: updatedOrder.status,
        paymentStatus: updatedOrder.paymentStatus,
        hasQRCode: !!updatedOrder.qrCodeDataUrl
      })
      
      // Debug: Check if the order was actually updated in the store
      const verifyOrder = Object.values(marketStore.orders).find(o => 
        o.id === paymentRequest.id || o.originalOrderId === paymentRequest.id
      )
      console.log('Verified order in store after update:', {
        found: !!verifyOrder,
        hasPaymentRequest: !!verifyOrder?.paymentRequest,
        paymentStatus: verifyOrder?.paymentStatus
      })
    } else {
      console.warn('Payment request received for unknown order:', paymentRequest.id)
    }
  }

  /**
   * Handle incoming order status update from merchant (type 2)
   */
  async handleOrderStatusUpdate(statusUpdate: NostrmarketOrderStatus): Promise<void> {
    console.log('Received order status update from merchant:', {
      orderId: statusUpdate.id,
      message: statusUpdate.message,
      paid: statusUpdate.paid,
      shipped: statusUpdate.shipped
    })

    const { useMarketStore } = await import('../stores/market')
    const marketStore = useMarketStore()
    
    const order = Object.values(marketStore.orders).find(o => 
      o.id === statusUpdate.id || o.originalOrderId === statusUpdate.id
    )
    
    if (order) {
      // Create the updated order object with all status updates
      const updatedOrder = {
        ...order,
        updatedAt: Math.floor(Date.now() / 1000),
        items: [...order.items], // Convert readonly to mutable
        shippingZone: order.shippingZone ? {
          ...order.shippingZone,
          countries: order.shippingZone.countries ? [...order.shippingZone.countries] : undefined
        } : order.shippingZone
      }
      
      // Update payment status
      if (statusUpdate.paid !== undefined) {
        updatedOrder.paid = statusUpdate.paid
        updatedOrder.paymentStatus = (statusUpdate.paid ? 'paid' : 'pending') as 'paid' | 'pending' | 'expired'
        updatedOrder.status = statusUpdate.paid ? 'paid' : 'pending'
        updatedOrder.paidAt = statusUpdate.paid ? Math.floor(Date.now() / 1000) : undefined
      }
      
      // Update shipping status
      if (statusUpdate.shipped !== undefined) {
        updatedOrder.shipped = statusUpdate.shipped
        if (statusUpdate.shipped && updatedOrder.status === 'paid') {
          updatedOrder.status = 'shipped'
        }
      }
      
      // Apply the update - this should trigger persistence
      marketStore.updateOrder(order.id, updatedOrder)
      
      console.log('Order status updated and persisted:', {
        orderId: statusUpdate.id,
        paid: updatedOrder.paid,
        shipped: updatedOrder.shipped,
        paymentStatus: updatedOrder.paymentStatus,
        status: updatedOrder.status,
        paidAt: updatedOrder.paidAt
      })
    } else {
      console.warn('Status update received for unknown order:', statusUpdate.id)
    }
  }

  /**
   * Publish all stalls and products for a merchant
   */
  async publishMerchantCatalog(stalls: Stall[], products: Product[]): Promise<{
    stalls: Record<string, string>, // stallId -> eventId
    products: Record<string, string> // productId -> eventId
  }> {
    const results = {
      stalls: {} as Record<string, string>,
      products: {} as Record<string, string>
    }

    // Publish stalls first
    for (const stall of stalls) {
      try {
        const eventId = await this.publishStall(stall)
        results.stalls[stall.id] = eventId
      } catch (error) {
        console.error(`Failed to publish stall ${stall.id}:`, error)
      }
    }

    // Publish products
    for (const product of products) {
      try {
        const eventId = await this.publishProduct(product)
        results.products[product.id] = eventId
      } catch (error) {
        console.error(`Failed to publish product ${product.id}:`, error)
      }
    }

    return results
  }
}
