/**
 * Omada Service
 * TP-Link Omada Controller integration service
 */

import { logger } from '../lib/logger.js'
import { Context } from '../types/context'

// Omada API client would be imported here
// import { OmadaController } from '../../infrastructure/omada/api/omada_client'

export interface OmadaConfig {
  host: string
  username: string
  password: string
  port?: number
  enabled: boolean
}

class OmadaService {
  private config: OmadaConfig | null = null
  private initialized: boolean = false

  constructor() {
    this.initializeConfig()
  }

  private initializeConfig(): void {
    const host = process.env.OMADA_CONTROLLER_HOST
    const username = process.env.OMADA_CONTROLLER_USERNAME
    const password = process.env.OMADA_CONTROLLER_PASSWORD

    if (host && username && password) {
      this.config = {
        host,
        username,
        password,
        port: parseInt(process.env.OMADA_CONTROLLER_PORT || '8043', 10),
        enabled: process.env.OMADA_ENABLED !== 'false',
      }
    } else {
      // Use environment variables with sensible defaults for development
      this.config = {
        host: process.env.OMADA_CONTROLLER_HOST || 'omada.example.com',
        username: process.env.OMADA_CONTROLLER_USERNAME || 'admin',
        password: process.env.OMADA_CONTROLLER_PASSWORD || '',
        port: parseInt(process.env.OMADA_CONTROLLER_PORT || '8043', 10),
        enabled: process.env.OMADA_ENABLED === 'true',
      }
      if (!this.config.enabled) {
        logger.warn('Omada controller not configured, service will operate in mock mode')
      }
    }
  }

  private token: string | null = null

  async initialize(): Promise<void> {
    if (!this.config?.enabled) {
      logger.info('Omada service is disabled')
      this.initialized = true
      return
    }

    try {
      // Authenticate with Omada API
      const baseUrl = `https://${this.config.host}:${this.config.port}`
      const loginResponse = await fetch(`${baseUrl}/api/v2/login`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          username: this.config.username,
          password: this.config.password,
        }),
      })

      if (!loginResponse.ok) {
        throw new Error(`Omada authentication failed: ${loginResponse.statusText}`)
      }

      const loginData = await loginResponse.json()
      this.token = loginData.token || loginData.result?.token || null
      
      if (!this.token) {
        throw new Error('Failed to obtain Omada authentication token')
      }
      
      logger.info('Omada service initialized', { host: this.config.host })
      this.initialized = true
    } catch (error) {
      logger.error('Failed to initialize Omada service', { error })
      throw error
    }
  }

  private async makeRequest(endpoint: string, method: string = 'GET', body?: unknown): Promise<unknown> {
    if (!this.config?.enabled || !this.token) {
      throw new Error('Omada service not initialized or not enabled')
    }

    const baseUrl = `https://${this.config.host}:${this.config.port}`
    const response = await fetch(`${baseUrl}${endpoint}`, {
      method,
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${this.token}`,
      },
      body: body ? JSON.stringify(body) : undefined,
    })

    if (!response.ok) {
      throw new Error(`Omada API request failed: ${response.statusText}`)
    }

    return response.json()
  }

  async getSites(context: Context): Promise<Array<{
    id: string
    name: string
    timezone: string
    description: string
    createdAt: Date
    updatedAt: Date
  }>> {
    if (!this.initialized) {
      await this.initialize()
    }

    if (!this.config?.enabled) {
      // Return mock data for development
      return [
        {
          id: 'site-1',
          name: 'Default Site',
          timezone: 'UTC',
          description: 'Default Omada site',
          createdAt: new Date(),
          updatedAt: new Date(),
        },
      ]
    }

    try {
      const data = await this.makeRequest('/api/v2/sites')
      const sites = (data.result || data.data || []) as Array<Record<string, unknown>>
      return sites.map((site) => ({
        id: site.id || site.siteId,
        name: site.name,
        timezone: site.timezone || 'UTC',
        description: site.description || '',
        createdAt: site.createdAt ? new Date(site.createdAt) : new Date(),
        updatedAt: site.updatedAt ? new Date(site.updatedAt) : new Date(),
      }))
    } catch (error) {
      logger.error('Failed to fetch Omada sites', { error })
      throw error
    }
  }

  async getSite(context: Context, siteId: string): Promise<{
    id: string
    name: string
    timezone: string
    description: string
    createdAt: Date
    updatedAt: Date
  }> {
    if (!this.initialized) {
      await this.initialize()
    }

    if (!this.config?.enabled) {
      return {
        id: siteId,
        name: 'Default Site',
        timezone: 'UTC',
        description: 'Default Omada site',
        createdAt: new Date(),
        updatedAt: new Date(),
      }
    }

    try {
      const data = await this.makeRequest(`/api/v2/sites/${siteId}`)
      const site = data.result || data.data
      return {
        id: site.id || site.siteId || siteId,
        name: site.name || 'Unknown Site',
        timezone: site.timezone || 'UTC',
        description: site.description || '',
        createdAt: site.createdAt ? new Date(site.createdAt) : new Date(),
        updatedAt: site.updatedAt ? new Date(site.updatedAt) : new Date(),
      }
    } catch (error) {
      logger.error('Failed to fetch Omada site', { siteId, error })
      throw error
    }
  }

  async getAccessPoints(context: Context, siteId: string): Promise<Array<{
    id: string
    name: string
    siteId: string
    macAddress: string
    model: string
    firmwareVersion: string
    status: string
    location: string
    radioConfig: Record<string, unknown>
    connectedClients: number
    createdAt: Date
    updatedAt: Date
  }>> {
    if (!this.initialized) {
      await this.initialize()
    }

    if (!this.config?.enabled) {
      return []
    }

    try {
      const data = await this.makeRequest(`/api/v2/sites/${siteId}/devices/accesspoints`)
      const aps = (data.result || data.data || []) as Array<Record<string, unknown>>
      return aps.map((ap) => ({
        id: ap.id || ap.macAddress,
        name: ap.name || ap.macAddress,
        siteId,
        macAddress: ap.macAddress || ap.mac,
        model: ap.model || '',
        firmwareVersion: ap.firmwareVersion || ap.version || '',
        status: ap.status === 'online' ? 'ONLINE' : ap.status === 'offline' ? 'OFFLINE' : 'UNKNOWN',
        location: ap.location || '',
        radioConfig: ap.radioConfig || {},
        connectedClients: ap.connectedClients || 0,
        createdAt: ap.createdAt ? new Date(ap.createdAt) : new Date(),
        updatedAt: ap.updatedAt ? new Date(ap.updatedAt) : new Date(),
      }))
    } catch (error) {
      logger.error('Failed to fetch Omada access points', { siteId, error })
      throw error
    }
  }

  async getAccessPoint(context: Context, apId: string): Promise<{
    id: string
    name: string
    siteId: string
    macAddress: string
    model: string
    firmwareVersion: string
    status: string
    location: string
    radioConfig: Record<string, unknown>
    connectedClients: number
    createdAt: Date
    updatedAt: Date
  }> {
    if (!this.initialized) {
      await this.initialize()
    }

    if (!this.config?.enabled) {
      throw new Error('Omada service not enabled')
    }

    try {
      // First get all sites to find which site contains this AP
      const sites = await this.getSites(context)
      for (const site of sites) {
        const aps = await this.getAccessPoints(context, site.id)
        const ap = aps.find((a) => (a.id === apId || a.macAddress === apId))
        if (ap) {
          return ap
        }
      }
      throw new Error(`Access point ${apId} not found`)
    } catch (error) {
      logger.error('Failed to fetch Omada access point', { apId, error })
      throw error
    }
  }

  async getClients(context: Context, siteId: string): Promise<Array<{
    id: string
    macAddress: string
    siteId: string
    name: string
    ipAddress: string
    connectedApId: string | null
    signalStrength: number | null
    connectedAt: Date | null
    lastSeen: Date
  }>> {
    if (!this.initialized) {
      await this.initialize()
    }

    if (!this.config?.enabled) {
      return []
    }

    try {
      const data = await this.makeRequest(`/api/v2/sites/${siteId}/clients`)
      const clients = (data.result || data.data || []) as Array<Record<string, unknown>>
      return clients.map((client) => ({
        id: client.id || client.macAddress,
        macAddress: client.macAddress || client.mac,
        siteId,
        name: client.name || client.hostname || '',
        ipAddress: client.ipAddress || client.ip || '',
        connectedApId: client.connectedApId || client.apId || null,
        signalStrength: client.signalStrength || client.rssi || null,
        connectedAt: client.connectedAt ? new Date(client.connectedAt) : null,
        lastSeen: client.lastSeen ? new Date(client.lastSeen) : new Date(),
      }))
    } catch (error) {
      logger.error('Failed to fetch Omada clients', { siteId, error })
      throw error
    }
  }

  async getClient(context: Context, mac: string): Promise<{
    id: string
    macAddress: string
    siteId: string
    name: string
    ipAddress: string
    connectedApId: string | null
    signalStrength: number | null
    connectedAt: Date | null
    lastSeen: Date
  }> {
    if (!this.initialized) {
      await this.initialize()
    }

    if (!this.config?.enabled) {
      throw new Error('Omada service not enabled')
    }

    try {
      // Search across all sites
      const sites = await this.getSites(context)
      for (const site of sites) {
        const clients = await this.getClients(context, site.id)
        const client = clients.find((c) => c.macAddress === mac)
        if (client) {
          return client
        }
      }
      throw new Error(`Client with MAC ${mac} not found`)
    } catch (error) {
      logger.error('Failed to fetch Omada client', { mac, error })
      throw error
    }
  }

  async getSsids(context: Context, siteId: string): Promise<Array<{
    id: string
    name: string
    siteId: string
    security: string
    enabled: boolean
    vlan: number | null
    radios: unknown[]
    createdAt: Date
    updatedAt: Date
  }>> {
    if (!this.initialized) {
      await this.initialize()
    }

    if (!this.config?.enabled) {
      return []
    }

    try {
      const data = await this.makeRequest(`/api/v2/sites/${siteId}/ssids`)
      const ssids = (data.result || data.data || []) as Array<Record<string, unknown>>
      return ssids.map((ssid) => ({
        id: ssid.id || ssid.name,
        name: ssid.name,
        siteId,
        security: ssid.security === 'wpa3' ? 'WPA3' : ssid.security === 'wpa2' ? 'WPA2' : 'OPEN',
        enabled: ssid.enabled !== false,
        vlan: ssid.vlan || null,
        radios: ssid.radios || [],
        createdAt: ssid.createdAt ? new Date(ssid.createdAt) : new Date(),
        updatedAt: ssid.updatedAt ? new Date(ssid.updatedAt) : new Date(),
      }))
    } catch (error) {
      logger.error('Failed to fetch Omada SSIDs', { siteId, error })
      throw error
    }
  }
}

// Singleton instance
export const omadaService = new OmadaService()

