import { NextRequest, NextResponse } from 'next/server';
import { Pool } from 'pg';
import { getAutotaskClient } from '@/lib/services/autotask-factory';
import { getDattoRMMClient } from '@/lib/services/datto-rmm-factory';
import { getAuvikClient } from '@/lib/services/auvik-factory';
import { getAddigyClient } from '@/lib/services/addigy-factory';
import { apiCache } from '@/lib/services/cache';
import { DattoRMMDevice } from '@/lib/types/datto-rmm';
import { AuvikDevice } from '@/lib/types/auvik';
import { AddigyDevice } from '@/lib/types/addigy';
import { ConfigurationItem } from '@/lib/types/autotask';

const pool = new Pool({
  host: process.env.POSTGRES_HOST,
  port: parseInt(process.env.POSTGRES_PORT || '5432'),
  database: process.env.POSTGRES_DB,
  user: process.env.POSTGRES_USER,
  password: process.env.POSTGRES_PASSWORD,
});

interface DeviceComparison {
  autotaskDevice?: ConfigurationItem;
  rmmDevice?: DattoRMMDevice;
  auvikDevice?: AuvikDevice;
  addigyDevice?: AddigyDevice;
  status: 'matched' | 'autotask-only' | 'rmm-only';
  matchedBy?: string; // What field was used to match
}

// Helper function to normalize MAC address for comparison
function normalizeMacAddress(mac: string): string {
  return mac.replace(/[:-]/g, '').toLowerCase();
}

// Helper function to match Addigy device to Autotask device
function matchAddigyDevice(
  autotaskDevice: ConfigurationItem,
  addigyDevices: AddigyDevice[]
): AddigyDevice | null {
  // Priority 1: Serial number (primary matching method for Apple devices)
  if (autotaskDevice.serialNumber) {
    const autotaskSerial = autotaskDevice.serialNumber?.toLowerCase().trim();
    console.log(`Trying to match Autotask device "${autotaskDevice.referenceTitle}" with serial: ${autotaskSerial}`);
    console.log(`Checking against ${addigyDevices.length} Addigy devices`);
    
    const match = addigyDevices.find((d) => {
      const addigySerial = d['Serial Number']?.toLowerCase().trim();
      if (addigySerial) {
        console.log(`  Comparing with Addigy device "${d['Device Name']}" serial: ${addigySerial}`);
      }
      return addigySerial === autotaskSerial;
    });
    
    if (match) {
      console.log(
        `✓ Matched Addigy device by serial: ${match['Device Name']} (${match['Serial Number']}) -> ${autotaskDevice.referenceTitle} (${autotaskDevice.serialNumber})`
      );
      return match;
    } else {
      console.log(`✗ No Addigy serial match found for ${autotaskDevice.serialNumber}`);
    }
  }

  // Priority 2: Device name/hostname
  const hostname =
    autotaskDevice.rmmDeviceAuditHostname || autotaskDevice.referenceTitle;
  if (hostname) {
    const match = addigyDevices.find(
      (d) =>
        d['Device Name']?.toLowerCase().trim() === hostname.toLowerCase().trim()
    );
    if (match) {
      console.log(
        `Matched Addigy device by name: ${match['Device Name']} -> ${autotaskDevice.referenceTitle}`
      );
      return match;
    }
  }

  return null;
}

// Helper function to match Auvik device to Autotask device
function matchAuvikDevice(
  autotaskDevice: ConfigurationItem,
  auvikDevices: AuvikDevice[]
): AuvikDevice | null {
  // Priority 1: Serial number
  if (autotaskDevice.serialNumber) {
    const match = auvikDevices.find(
      (d) =>
        d.serialNumber?.toLowerCase().trim() ===
        autotaskDevice.serialNumber?.toLowerCase().trim()
    );
    if (match) {
      console.log(
        `Matched Auvik device by serial: ${match.deviceName} -> ${autotaskDevice.referenceTitle}`
      );
      return match;
    }
  }

  // Priority 2: Hostname
  const hostname =
    autotaskDevice.rmmDeviceAuditHostname || autotaskDevice.referenceTitle;
  if (hostname) {
    const match = auvikDevices.find(
      (d) =>
        d.deviceName?.toLowerCase().trim() === hostname.toLowerCase().trim()
    );
    if (match) {
      console.log(
        `Matched Auvik device by hostname: ${match.deviceName} -> ${autotaskDevice.referenceTitle}`
      );
      return match;
    }
  }

  // Priority 3: MAC address
  const macAddress = autotaskDevice.rmmDeviceAuditMacAddress;
  if (macAddress && macAddress.length > 0) {
    const normalizedMac = normalizeMacAddress(macAddress);
    const match = auvikDevices.find((d) =>
      d.macAddresses?.some(
        (mac) => normalizeMacAddress(mac) === normalizedMac
      )
    );
    if (match) {
      console.log(
        `Matched Auvik device by MAC: ${match.deviceName} -> ${autotaskDevice.referenceTitle}`
      );
      return match;
    }
  }

  return null;
}

export async function GET(request: NextRequest) {
  try {
    const searchParams = request.nextUrl.searchParams;
    const companyId = searchParams.get('companyId');
    const companyName = searchParams.get('companyName');
    const activeFilter = searchParams.get('activeFilter') || 'active';
    const skipCache = searchParams.get('skipCache') === 'true';
    
    // Get mapping counts to include in cache key (so cache invalidates when mappings change)
    let rmmMappingCount = 0;
    let auvikMappingCount = 0;
    let addigyMappingCount = 0;
    if (companyId) {
      const rmmMappingsResult = await pool.query(
        'SELECT COUNT(*) as count FROM rmm_site_mappings WHERE company_id = $1',
        [parseInt(companyId)]
      );
      rmmMappingCount = parseInt(rmmMappingsResult.rows[0]?.count || '0');
      
      const auvikMappingsResult = await pool.query(
        'SELECT COUNT(*) as count FROM auvik_tenant_mappings WHERE autotask_company_id = $1',
        [parseInt(companyId)]
      );
      auvikMappingCount = parseInt(auvikMappingsResult.rows[0]?.count || '0');
      
      const addigyMappingsResult = await pool.query(
        'SELECT COUNT(*) as count FROM addigy_org_mappings WHERE autotask_company_id = $1',
        [parseInt(companyId)]
      );
      addigyMappingCount = parseInt(addigyMappingsResult.rows[0]?.count || '0');
    }
    
    // Check cache first (unless skipCache is true)
    const cacheKey = `rmm-devices:${companyId}:${activeFilter}:rmm-${rmmMappingCount}:auvik-${auvikMappingCount}:addigy-${addigyMappingCount}`;
    if (!skipCache) {
      const cached = apiCache.get(cacheKey);
      if (cached) {
        console.log(`Cache hit for ${cacheKey}`);
        return NextResponse.json(cached);
      }
    } else {
      console.log(`Skipping cache for ${cacheKey}`);
    }

    if (!companyId) {
      return NextResponse.json({ 
        rmmDevices: [],
        autotaskDevices: [],
        comparison: [],
        message: 'Please select a company to view devices'
      });
    }

    // Get Autotask devices based on active filter
    const autotaskClient = getAutotaskClient();
    let autotaskDevices: ConfigurationItem[] = [];
    
    if (activeFilter === 'all') {
      // Get all devices regardless of status
      const allItems = await autotaskClient.queryEntity<ConfigurationItem>('ConfigurationItems', {
        filter: [{ op: 'eq', field: 'companyID', value: parseInt(companyId) }],
      });
      autotaskDevices = allItems;
    } else if (activeFilter === 'inactive') {
      // Get only inactive devices
      const inactiveItems = await autotaskClient.queryEntity<ConfigurationItem>('ConfigurationItems', {
        filter: [
          { op: 'eq', field: 'companyID', value: parseInt(companyId) },
          { op: 'eq', field: 'isActive', value: false }
        ],
      });
      autotaskDevices = inactiveItems;
    } else {
      // Default: get only active devices
      autotaskDevices = await autotaskClient.getConfigurationItemsByCompany(parseInt(companyId));
    }

    // Get RMM devices
    let rmmDevices: DattoRMMDevice[] = [];
    try {
      const rmmClient = getDattoRMMClient();
      
      // First, check if we have site mappings for this company
      if (companyId) {
        const mappingsResult = await pool.query(
          'SELECT rmm_site_uid FROM rmm_site_mappings WHERE company_id = $1',
          [parseInt(companyId)]
        );
        
        if (mappingsResult.rows.length > 0) {
          // Use the new multi-site method if mappings exist
          const siteUids = mappingsResult.rows.map(row => row.rmm_site_uid);
          console.log(`Found ${siteUids.length} mapped RMM sites for company ${companyId}`);
          rmmDevices = await rmmClient.getDevicesForSites(siteUids);
        } else if (companyName) {
          // Fall back to old method if no mappings exist
          console.log(`No RMM site mappings found for company ${companyId}, using name-based matching`);
          rmmDevices = await rmmClient.getDevicesByCompanyName(companyName);
        }
      } else if (companyName) {
        // Try to get devices by company name (matching site name)
        rmmDevices = await rmmClient.getDevicesByCompanyName(companyName);
      } else {
        // If no company info, get all devices and try to match
        rmmDevices = await rmmClient.getAllDevices();
      }
      
      // Filter RMM devices based on activeFilter
      if (activeFilter === 'active') {
        // Only show non-deleted, non-suspended RMM devices when filtering for active
        rmmDevices = rmmDevices.filter(device => !device.deleted && !device.suspended);
      } else if (activeFilter === 'inactive') {
        // Only show deleted or suspended RMM devices when filtering for inactive
        rmmDevices = rmmDevices.filter(device => device.deleted || device.suspended);
      }
      // If 'all', show all RMM devices (no filtering)
      
      // Deduplicate RMM devices by ID (in case the same device appears in multiple sites)
      const uniqueRmmDevices = new Map<string, DattoRMMDevice>();
      rmmDevices.forEach(device => {
        const deviceId = String(device.id);
        if (!uniqueRmmDevices.has(deviceId)) {
          uniqueRmmDevices.set(deviceId, device);
        }
      });
      rmmDevices = Array.from(uniqueRmmDevices.values());
      console.log(`After deduplication: ${rmmDevices.length} unique RMM devices`)
      
    } catch (rmmError) {
      console.error('Error fetching RMM devices:', rmmError);
      // Continue with empty RMM devices array
    }

    // Get Auvik devices
    let auvikDevices: AuvikDevice[] = [];
    try {
      const auvikClient = getAuvikClient();
      
      if (companyId) {
        // Try to find tenant using company ID mapping first (most accurate)
        const tenant = await auvikClient.findTenantByCompanyId(parseInt(companyId));
        
        if (tenant) {
          console.log(`Found Auvik tenant via mapping: ${tenant.domainPrefix} for company ID: ${companyId}`);
          auvikDevices = await auvikClient.getDevicesByTenant(tenant.id);
        } else if (companyName) {
          // Fallback to name-based matching
          console.log(`No mapping found, trying name match for: ${companyName}`);
          const tenantByName = await auvikClient.findTenantByName(companyName);
          if (tenantByName) {
            console.log(`Found Auvik tenant by name: ${tenantByName.domainPrefix} for company: ${companyName}`);
            auvikDevices = await auvikClient.getDevicesByTenant(tenantByName.id);
          } else {
            console.log(`No Auvik tenant found for company: ${companyName}`);
          }
        }
      } else if (companyName) {
        // If no company ID, try name matching
        const tenant = await auvikClient.findTenantByName(companyName);
        if (tenant) {
          console.log(`Found Auvik tenant: ${tenant.domainPrefix} for company: ${companyName}`);
          auvikDevices = await auvikClient.getDevicesByTenant(tenant.id);
        }
      } else {
        // If no company info, get all devices
        auvikDevices = await auvikClient.getAllDevices();
      }
      
      console.log(`Fetched ${auvikDevices.length} Auvik devices before filtering`);
      
      // Filter Auvik devices to only show those with valid hostnames
      // Exclude devices without deviceName or with names starting with "Device@"
      auvikDevices = auvikDevices.filter(device => {
        if (!device.deviceName) {
          return false;
        }
        if (device.deviceName.startsWith('Device@')) {
          return false;
        }
        return true;
      });
      
      console.log(`Filtered to ${auvikDevices.length} Auvik devices with valid hostnames`);
    } catch (auvikError) {
      console.error('Error fetching Auvik devices:', auvikError);
      // Continue with empty Auvik devices array
    }

    // Get Addigy devices (Apple RMM)
    let addigyDevices: AddigyDevice[] = [];
    try {
      const addigyClient = getAddigyClient();
      
      if (companyId) {
        // Try to find policy using company ID mapping first
        const mappingsResult = await pool.query(
          'SELECT addigy_org_id FROM addigy_org_mappings WHERE autotask_company_id = $1',
          [parseInt(companyId)]
        );
        
        if (mappingsResult.rows.length > 0) {
          // Get all devices and filter by policy IDs in code
          const policyIds = new Set(mappingsResult.rows.map(row => row.addigy_org_id));
          console.log(`Found ${policyIds.size} mapped Addigy policies for company ${companyId}:`, Array.from(policyIds));
          
          // Fetch all devices (without filter)
          const allDevices = await addigyClient.getAllDevices();
          console.log(`Fetched ${allDevices.length} total Addigy devices`);
          
          // Filter devices by policy_id in code
          addigyDevices = allDevices.filter(device => policyIds.has(device.policy_id));
          console.log(`Filtered to ${addigyDevices.length} devices matching mapped policies`);
        } else {
          console.log(`No Addigy policy mappings found for company ${companyId}`);
        }
      }
      
      console.log(`Final Addigy devices count: ${addigyDevices.length}`);
    } catch (addigyError) {
      console.error('Error fetching Addigy devices:', addigyError);
      // Continue with empty Addigy devices array
    }

    // Compare and match devices
    const comparison: DeviceComparison[] = [];
    const matchedAutotaskIds = new Set<number>();
    const matchedRmmIds = new Set<string>();

    // Try to match devices
    for (const rmmDevice of rmmDevices) {
      // Skip if this RMM device has already been matched
      if (matchedRmmIds.has(String(rmmDevice.id))) {
        continue;
      }
      
      let matched = false;
      
      // Try to match by RMM Device UID
      if (rmmDevice.uid) {
        const autotaskMatch = autotaskDevices.find(
          at => at.rmmDeviceUID === rmmDevice.uid && !matchedAutotaskIds.has(at.id)
        );
        
        if (autotaskMatch) {
          comparison.push({
            autotaskDevice: autotaskMatch,
            rmmDevice: rmmDevice,
            status: 'matched',
            matchedBy: 'RMM UID'
          });
          matchedAutotaskIds.add(autotaskMatch.id);
          matchedRmmIds.add(String(rmmDevice.id));
          matched = true;
        }
      }
      
      // Try to match by serial number
      if (!matched && rmmDevice.serialNumber) {
        const autotaskMatch = autotaskDevices.find(
          at => (at.serialNumber === rmmDevice.serialNumber || 
                 at.dattoSerialNumber === rmmDevice.serialNumber) && 
                !matchedAutotaskIds.has(at.id)
        );
        
        if (autotaskMatch) {
          comparison.push({
            autotaskDevice: autotaskMatch,
            rmmDevice: rmmDevice,
            status: 'matched',
            matchedBy: 'Serial Number'
          });
          matchedAutotaskIds.add(autotaskMatch.id);
          matchedRmmIds.add(String(rmmDevice.id));
          matched = true;
        }
      }
      
      // Try to match by hostname
      if (!matched && rmmDevice.hostname) {
        const autotaskMatch = autotaskDevices.find(
          at => (at.rmmDeviceAuditHostname?.toLowerCase() === rmmDevice.hostname.toLowerCase() ||
                 at.dattoHostname?.toLowerCase() === rmmDevice.hostname.toLowerCase() ||
                 at.referenceTitle?.toLowerCase().includes(rmmDevice.hostname.toLowerCase())) && 
                !matchedAutotaskIds.has(at.id)
        );
        
        if (autotaskMatch) {
          comparison.push({
            autotaskDevice: autotaskMatch,
            rmmDevice: rmmDevice,
            status: 'matched',
            matchedBy: 'Hostname'
          });
          matchedAutotaskIds.add(autotaskMatch.id);
          matchedRmmIds.add(String(rmmDevice.id));
          matched = true;
        }
      }
      
      // Try to match by IP address
      if (!matched && (rmmDevice.intIpAddress || rmmDevice.extIpAddress)) {
        const autotaskMatch = autotaskDevices.find(
          at => (at.rmmDeviceAuditIPAddress === rmmDevice.intIpAddress ||
                 at.rmmDeviceAuditIPAddress === rmmDevice.extIpAddress ||
                 at.dattoInternalIP === rmmDevice.intIpAddress ||
                 at.dattoRemoteIP === rmmDevice.extIpAddress) && 
                !matchedAutotaskIds.has(at.id)
        );
        
        if (autotaskMatch) {
          comparison.push({
            autotaskDevice: autotaskMatch,
            rmmDevice: rmmDevice,
            status: 'matched',
            matchedBy: 'IP Address'
          });
          matchedAutotaskIds.add(autotaskMatch.id);
          matchedRmmIds.add(String(rmmDevice.id));
          matched = true;
        }
      }
      
      // If no match found, add as RMM-only
      if (!matched) {
        comparison.push({
          rmmDevice: rmmDevice,
          status: 'rmm-only'
        });
      }
    }
    
    // Add Autotask-only devices and match with Auvik and Addigy
    for (const autotaskDevice of autotaskDevices) {
      if (!matchedAutotaskIds.has(autotaskDevice.id)) {
        // Try to match with Auvik device
        const auvikMatch = matchAuvikDevice(autotaskDevice, auvikDevices);
        // Try to match with Addigy device
        const addigyMatch = matchAddigyDevice(autotaskDevice, addigyDevices);
        
        comparison.push({
          autotaskDevice: autotaskDevice,
          auvikDevice: auvikMatch || undefined,
          addigyDevice: addigyMatch || undefined,
          status: 'autotask-only'
        });
      } else {
        // For already matched devices, also try to match with Auvik and Addigy
        const existingComparison = comparison.find(
          (c) => c.autotaskDevice?.id === autotaskDevice.id
        );
        if (existingComparison) {
          if (!existingComparison.auvikDevice) {
            const auvikMatch = matchAuvikDevice(autotaskDevice, auvikDevices);
            if (auvikMatch) {
              existingComparison.auvikDevice = auvikMatch;
            }
          }
          if (!existingComparison.addigyDevice) {
            const addigyMatch = matchAddigyDevice(autotaskDevice, addigyDevices);
            if (addigyMatch) {
              existingComparison.addigyDevice = addigyMatch;
            }
          }
        }
      }
    }

    // Track which Auvik and Addigy devices have been matched
    const matchedAuvikIds = new Set<string>();
    const matchedAddigyIds = new Set<string>();
    
    comparison.forEach(item => {
      if (item.auvikDevice?.id) {
        matchedAuvikIds.add(item.auvikDevice.id);
      }
      if (item.addigyDevice?.agentid) {
        matchedAddigyIds.add(item.addigyDevice.agentid);
      }
    });

    // Skip unmatched Auvik devices (NMS-only) - don't add them to comparison
    // They will still be counted in stats but won't appear in the device list
    for (const auvikDevice of auvikDevices) {
      if (!matchedAuvikIds.has(auvikDevice.id)) {
        // Mark as matched so it's counted but don't add to comparison
        matchedAuvikIds.add(auvikDevice.id);
      }
    }

    // Add unmatched Addigy devices (ARMM-only)
    for (const addigyDevice of addigyDevices) {
      if (!matchedAddigyIds.has(addigyDevice.agentid)) {
        comparison.push({
          addigyDevice: addigyDevice,
          status: 'rmm-only' // Using rmm-only status for non-PSA devices
        });
      }
    }

    // Sort comparison results
    comparison.sort((a, b) => {
      // Sort by status first (matched, then autotask-only, then rmm-only)
      const statusOrder = { 'matched': 0, 'autotask-only': 1, 'rmm-only': 2 };
      const statusDiff = statusOrder[a.status] - statusOrder[b.status];
      if (statusDiff !== 0) return statusDiff;
      
      // Then sort by device name (check all possible sources)
      const aName = a.autotaskDevice?.referenceTitle || 
                    a.rmmDevice?.hostname || 
                    a.auvikDevice?.deviceName || 
                    a.addigyDevice?.['Device Name'] || 
                    '';
      const bName = b.autotaskDevice?.referenceTitle || 
                    b.rmmDevice?.hostname || 
                    b.auvikDevice?.deviceName || 
                    b.addigyDevice?.['Device Name'] || 
                    '';
      return aName.localeCompare(bName);
    });

    // Fetch contacts for the company to avoid individual API calls
    const contacts: Record<number, any> = {};
    try {
      const contactIds = new Set<number>();
      autotaskDevices.forEach(device => {
        if (device.contactID) {
          contactIds.add(device.contactID);
        }
      });
      
      if (contactIds.size > 0) {
        console.log(`Fetching ${contactIds.size} contacts for company ${companyId}`);
        
        // Fetch all contacts for the company in one query
        const companyContacts = await autotaskClient.queryEntity('Contacts', {
          filter: [{ op: 'eq', field: 'companyID', value: parseInt(companyId) }],
        });
        
        // Map contacts by ID
        companyContacts.forEach((contact: any) => {
          contacts[contact.id] = contact;
        });
        
        console.log(`Fetched ${Object.keys(contacts).length} contacts`);
      }
    } catch (contactError) {
      console.error('Error fetching contacts:', contactError);
      // Continue without contacts
    }

    const response = { 
      rmmDevices,
      autotaskDevices,
      auvikDevices,
      addigyDevices,
      comparison,
      contacts, // Include contacts in response
      stats: {
        totalRmm: rmmDevices.length,
        totalAutotask: autotaskDevices.length,
        totalAuvik: auvikDevices.length,
        totalAddigy: addigyDevices.length,
        matched: comparison.filter(c => c.status === 'matched').length,
        autotaskOnly: comparison.filter(c => c.status === 'autotask-only').length,
        rmmOnly: comparison.filter(c => c.status === 'rmm-only').length,
      }
    };
    
    // Cache for 2 minutes
    apiCache.set(cacheKey, response, 120); // 120 seconds = 2 minutes
    
    return NextResponse.json(response);
  } catch (error) {
    console.error('Error in RMM devices endpoint:', error);
    
    if (error instanceof Error) {
      return NextResponse.json(
        { 
          error: 'Failed to fetch devices',
          message: error.message,
        },
        { status: 500 }
      );
    }
    
    return NextResponse.json(
      { error: 'An unexpected error occurred' },
      { status: 500 }
    );
  }
}
