<?php

namespace App\Console\Commands;

use App\Models\Dect\DectDiscovery;
use App\Models\Extension;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class DectDiscoveryCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'dect:discover';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Scan PJSIP endpoints and database for DECT devices';

    /**
     * DECT device patterns for detection
     */
    protected $dectPatterns = [
        // Yealink Base Stations
        'W70B' => ['type' => 'base_station', 'vendor' => 'Yealink'],
        'W80B' => ['type' => 'base_station', 'vendor' => 'Yealink'],
        'W90B' => ['type' => 'base_station', 'vendor' => 'Yealink'],
        // Yealink Handsets
        'W56H' => ['type' => 'handset', 'vendor' => 'Yealink'],
        'W57R' => ['type' => 'handset', 'vendor' => 'Yealink'],
        'W59R' => ['type' => 'handset', 'vendor' => 'Yealink'],
        'W73H' => ['type' => 'handset', 'vendor' => 'Yealink'],
        'W78H' => ['type' => 'handset', 'vendor' => 'Yealink'],
        'W79P' => ['type' => 'handset', 'vendor' => 'Yealink'],
        // Grandstream Base Stations
        'DP750' => ['type' => 'base_station', 'vendor' => 'Grandstream'],
        'DP752' => ['type' => 'base_station', 'vendor' => 'Grandstream'],
        'DP755' => ['type' => 'base_station', 'vendor' => 'Grandstream'],
    ];

    /**
     * Execute the console command.
     */
    public function handle()
    {
        $this->info('Starting DECT device discovery...');
        
        $discovered = 0;
        $errors = 0;
        
        try {
            // Scan Asterisk PJSIP contacts
            $this->info('Scanning Asterisk PJSIP contacts...');
            $asteriskDevices = $this->scanAsteriskPJSIP();
            $discovered += count($asteriskDevices);
            
            // Scan database tables
            $this->info('Scanning database tables...');
            $dbDevices = $this->scanDatabaseTables();
            $discovered += count($dbDevices);
            
            // Merge and deduplicate results
            $allDevices = array_merge($asteriskDevices, $dbDevices);
            $uniqueDevices = $this->deduplicateDevices($allDevices);
            
            // Store discovered devices
            foreach ($uniqueDevices as $device) {
                $this->storeDiscoveredDevice($device);
            }
            
            $this->info("Discovery completed. Found {$discovered} devices.");
            $this->info("Unique devices stored: " . count($uniqueDevices));
            
            return Command::SUCCESS;
        } catch (\Exception $e) {
            $this->error("Discovery failed: " . $e->getMessage());
            Log::error('DECT Discovery failed', ['error' => $e->getMessage()]);
            return Command::FAILURE;
        }
    }

    /**
     * Scan Asterisk PJSIP contacts for DECT devices
     */
    protected function scanAsteriskPJSIP()
    {
        $devices = [];
        
        try {
            // Get PJSIP contacts output
            $output = shell_exec('asterisk -rx "pjsip show contacts" 2>/dev/null');
            
            if ($output) {
                $lines = explode("\n", $output);
                
                foreach ($lines as $line) {
                    // Parse contact lines
                    if (preg_match('/Contact:\s+(\S+)\/sip:\S+@([\d\.]+):/', $line, $matches)) {
                        $contactId = $matches[1];
                        
                        // Get detailed contact info including user_agent
                        $detailOutput = shell_exec("asterisk -rx \"pjsip show contact {$contactId}\" 2>/dev/null");
                        
                        if ($detailOutput && preg_match('/user_agent\s*:\s*(.+)/i', $detailOutput, $uaMatch)) {
                            $userAgent = trim($uaMatch[1]);
                            $device = $this->analyzeUserAgent($userAgent, $contactId);
                            
                            if ($device) {
                                // Extract IP and URI from contact line
                                if (preg_match('/sip:' . preg_quote($contactId, '/') . '@([\d\.]+):/', $line, $ipMatch)) {
                                    $device['ip_address'] = $ipMatch[1];
                                }
                                
                                $devices[] = $device;
                            }
                        }
                    }
                }
            }
        } catch (\Exception $e) {
            $this->error("Error scanning Asterisk PJSIP: " . $e->getMessage());
        }
        
        return $devices;
    }

    /**
     * Scan database tables for DECT devices
     */
    protected function scanDatabaseTables()
    {
        $devices = [];
        
        try {
            // Scan ps_contacts table for user_agent
            $contacts = DB::table('ps_contacts')
                ->select('id', 'uri', 'user_agent', 'endpoint')
                ->whereNotNull('user_agent')
                ->get();
            
            foreach ($contacts as $contact) {
                $device = $this->analyzeUserAgent($contact->user_agent, $contact->endpoint);
                
                if ($device) {
                    // Decode URL-encoded semicolons (^3B = ;) then prefer
                    // x-ast-orig-host (real LAN IP) over the NAT-translated URI IP
                    $decodedUri = str_replace('^3B', ';', $contact->uri);
                    if (preg_match('/x-ast-orig-host=([\d\.]+)[:;]/', $decodedUri, $origMatch)) {
                        $device['ip_address'] = $origMatch[1];
                    } elseif (preg_match('/@([\d\.]+):/', $decodedUri, $ipMatch)) {
                        $device['ip_address'] = $ipMatch[1];
                    }
                    
                    $devices[] = $device;
                }
            }
            
            // Scan ps_endpoints for additional info
            $endpoints = DB::table('ps_endpoints')
                ->select('id', 'callerid')
                ->get();
            
            foreach ($endpoints as $endpoint) {
                // Try to match with existing devices
                foreach ($devices as &$device) {
                    if (isset($device['extension']) && $device['extension'] == $endpoint->id) {
                        // Extract name from callerid if available
                        if ($endpoint->callerid && preg_match('/"([^"]+)"/', $endpoint->callerid, $nameMatch)) {
                            $device['caller_name'] = $nameMatch[1];
                        }
                    }
                }
            }
        } catch (\Exception $e) {
            $this->error("Error scanning database: " . $e->getMessage());
        }
        
        return $devices;
    }

    /**
     * Analyze User-Agent string to identify DECT devices
     */
    protected function analyzeUserAgent($userAgent, $contactId = null)
    {
        $userAgent = strtoupper($userAgent);
        
        foreach ($this->dectPatterns as $pattern => $info) {
            if (strpos($userAgent, $pattern) !== false) {
                $device = [
                    'device_type' => $info['type'],
                    'model' => $pattern,
                    'manufacturer' => $info['vendor'],
                    'user_agent' => $userAgent,
                    'contact_id' => $contactId,
                    'ip_address' => null,
                    'extension_id' => null,
                ];
                
                // Try to extract extension from contact ID
                if ($contactId && is_numeric($contactId)) {
                    $extension = Extension::find($contactId);
                    if ($extension) {
                        $device['extension_id'] = $extension->id;
                        $device['extension'] = $extension->extension;
                    }
                }
                
                return $device;
            }
        }
        
        return null;
    }

    /**
     * Deduplicate devices based on IP and model
     */
    protected function deduplicateDevices($devices)
    {
        $unique = [];
        $seen = [];
        
        foreach ($devices as $device) {
            $key = ($device['ip_address'] ?? 'unknown') . '_' . $device['model'];
            
            if (!isset($seen[$key])) {
                $seen[$key] = true;
                $unique[] = $device;
            }
        }
        
        return $unique;
    }

    /**
     * Store discovered device in database
     */
    protected function storeDiscoveredDevice($device)
    {
        try {
            // Check if device already exists
            $existing = DectDiscovery::where('ip_address', $device['ip_address'] ?? null)
                ->where('model', $device['model'])
                ->first();
            
            if ($existing) {
                // Update last seen
                $existing->update([
                    'last_seen' => now(),
                    'status' => 'discovered',
                ]);
            } else {
                // Create new discovery record
                DectDiscovery::create([
                    'device_type' => $device['device_type'],
                    'model' => $device['model'],
                    'manufacturer' => $device['manufacturer'],
                    'ip_address' => $device['ip_address'],
                    'user_agent' => $device['user_agent'],
                    'status' => 'discovered',
                    'extension_id' => $device['extension_id'],
                    'raw_data' => $device,
                    'last_seen' => now(),
                ]);
            }
        } catch (\Exception $e) {
            $this->error("Error storing device: " . $e->getMessage());
        }
    }
}
