/**
 * Asset Sync Service
 * 
 * Mục đích: Đồng bộ thông tin thiết bị từ Meteocontrol Widget API vào hồ sơ tài sản
 * 
 * Chức năng:
 * - Đọc System Information từ Meteocontrol Widget API
 * - Parse modules (tấm pin) và inverters data
 * - Tự động tạo/cập nhật Assets (Plant, Inverter, Panel) trong MongoDB
 * - Mapping dữ liệu Meteocontrol với Asset model
 */

import { Project } from '../models/Project';
import { Asset } from '../models/Asset';
import { SystemSettings } from '../models/SystemSettings';
import mongoose from 'mongoose';

// Ensure fetch is available (Node.js 18+ has built-in fetch)
let fetch: typeof globalThis.fetch;
if (typeof globalThis.fetch === 'function') {
  fetch = globalThis.fetch;
} else {
  // Fallback for older Node.js versions
  try {
    const nodeFetch = require('node-fetch');
    fetch = nodeFetch as typeof globalThis.fetch;
  } catch (error) {
    throw new Error('fetch is not available. Please use Node.js 18+ or install node-fetch');
  }
}

const WIDGET_API_BASE_URL = 'http://ws.meteocontrol.de/api';
const V2_API_BASE_URL = 'https://api.meteocontrol.de/v2';

interface SystemInformation {
  siteDataCollection: {
    [siteKey: string]: {
      siteName?: string;
      nominalDCOutput?: number; // kWp
      location?: {
        latitude?: number;
        longitude?: number;
        street?: string;
        city?: string;
        country?: string;
      };
      modules?: {
        [model: string]: number; // quantity
      };
      inverters?: {
        [model: string]: number; // quantity
      };
      startupDate?: string; // German format: "20. Dezember 2024"
    };
  };
}

/**
 * Parse German date format to Date object
 * Example: "20. Dezember 2024" -> Date
 */
function parseGermanDate(dateStr: string): Date | null {
  try {
    const months: { [key: string]: number } = {
      januar: 0, februar: 1, märz: 2, april: 3, mai: 4, juni: 5,
      juli: 6, august: 7, september: 8, oktober: 9, november: 10, dezember: 11,
    };

    const parts = dateStr.toLowerCase().split(' ');
    if (parts.length !== 3) return null;

    const day = parseInt(parts[0].replace('.', ''));
    const monthName = parts[1];
    const year = parseInt(parts[2]);

    const month = months[monthName];
    if (month === undefined) return null;

    return new Date(year, month, day);
  } catch {
    return null;
  }
}

/**
 * Parse inverter model name to extract manufacturer and model
 * Example: "Huawei SUN2000-115KTL-M2 (400V)" -> { manufacturer: "Huawei", model: "SUN2000-115KTL-M2", voltage: "400V" }
 */
function parseInverterModel(modelName: string): { manufacturer: string; model: string; capacityKW?: number } {
  // Try to extract capacity from model name (e.g., "115KTL" -> 115 kW)
  const capacityMatch = modelName.match(/(\d+)KTL/i);
  const capacityKW = capacityMatch ? parseInt(capacityMatch[1]) : undefined;

  // Split by space to get manufacturer and model
  const parts = modelName.split(' ');
  const manufacturer = parts[0] || '';
  const model = parts.slice(1).join(' ').replace(/\s*\([^)]*\)\s*/g, '').trim(); // Remove voltage info in parentheses

  return { manufacturer, model, capacityKW };
}

/**
 * Parse panel model name to extract manufacturer and model
 * Example: "Jinko Solar JKM-585N-72HL4" -> { manufacturer: "Jinko Solar", model: "JKM-585N-72HL4", capacityW: 585 }
 */
function parsePanelModel(modelName: string): { manufacturer: string; model: string; capacityW?: number } {
  // Try to extract capacity from model name (e.g., "JKM-585N" -> 585 W)
  const capacityMatch = modelName.match(/-(\d+)N/i) || modelName.match(/-(\d+)W/i);
  const capacityW = capacityMatch ? parseInt(capacityMatch[1]) : undefined;

  // Split by space to get manufacturer and model
  const parts = modelName.split(' ');
  const manufacturer = parts[0] || '';
  const model = parts.slice(1).join(' ').trim();

  return { manufacturer, model, capacityW };
}

/**
 * Build a safe, unique-ish code fragment from an arbitrary identifier.
 * Always returns a non-empty token so generated asset codes never collide
 * just because a model/name happens to be empty.
 */
function slugifyCode(value: string | null | undefined, maxLen = 24): string {
  const slug = String(value ?? '')
    .replace(/[^a-zA-Z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '')
    .toUpperCase()
    .substring(0, maxLen);
  return slug || 'NA';
}

/**
 * Get System Information from Meteocontrol Widget API
 */
async function getSystemInformation(siteKey: string, apiKey: string): Promise<SystemInformation | null> {
  try {
    const url = `${WIDGET_API_BASE_URL}/sites/${siteKey}/widget?apiKey=${apiKey}`;
    
    // Use fetch with timeout wrapper
    const fetchWithTimeout = async (url: string, timeoutMs: number = 30000) => {
      return Promise.race([
        fetch(url, {
          headers: {
            'Accept': 'application/json',
          },
        }),
        new Promise<never>((_, reject) =>
          setTimeout(() => reject(new Error('Request timeout')), timeoutMs)
        ),
      ]);
    };

    const response = await fetchWithTimeout(url, 30000);

    if (!response.ok) {
      const errorText = await response.text().catch(() => 'Unable to read error response');
      // console.error(`[Asset Sync] Widget API error: ${response.status} ${errorText}`);
      return null;
    }

    const data = await response.json().catch((_err) => {
      // console.error(`[Asset Sync] Error parsing JSON response:`, err.message);
      return null;
    });

    return data as SystemInformation | null;
  } catch (error: any) {
    if (error.message === 'Request timeout') {
      // console.error(`[Asset Sync] Request timeout for site ${siteKey}`);
    } else {
      // console.error(`[Asset Sync] Error getting system information:`, error.message);
    }
    return null;
  }
}

/**
 * Find or create Plant Asset for a project
 */
async function findOrCreatePlantAsset(
  projectId: string,
  systemInfo: SystemInformation['siteDataCollection'][string]
): Promise<string | null> {
  try {
    // Find existing Plant Asset
    let plantAsset = await Asset.findOne({
      projectId,
      assetType: 'Plant',
    });

    const siteName = systemInfo.siteName || 'Solar Plant';
    const capacityKW = (systemInfo.nominalDCOutput || 0) * 1000; // Convert kWp to W (or keep as kW)
    const locationGPS = systemInfo.location?.latitude && systemInfo.location?.longitude
      ? { lat: systemInfo.location.latitude, lng: systemInfo.location.longitude }
      : undefined;
    const commissioningDate = systemInfo.startupDate ? parseGermanDate(systemInfo.startupDate) : undefined;

    if (plantAsset) {
      // Update existing Plant Asset
      plantAsset.name = siteName;
      plantAsset.capacityKW = capacityKW;
      plantAsset.locationGPS = locationGPS;
      if (commissioningDate) {
        plantAsset.commissioningDate = commissioningDate;
      }
      if (!plantAsset.specifications) {
        plantAsset.specifications = {};
      }
      plantAsset.specifications.syncedFromMeteocontrol = true;
      await plantAsset.save();
      return plantAsset._id.toString();
    } else {
      // Create new Plant Asset
      const code = `PLANT-${projectId.toString().slice(-6)}`;
      plantAsset = await Asset.create({
        projectId,
        assetType: 'Plant',
        name: siteName,
        code,
        capacityKW,
        locationGPS,
        commissioningDate,
        status: 'active',
        specifications: {
          syncedFromMeteocontrol: true,
        },
      });
      return plantAsset._id.toString();
    }
  } catch (error: any) {
    // console.error(`[Asset Sync] Error finding/creating Plant Asset:`, error.message);
    return null;
  }
}

/**
 * Get OAuth access token from Meteocontrol V2 API
 */
async function getOAuthToken(credentials: { username: string; password: string; apiKey: string }): Promise<string | null> {
  try {
    const response = await fetch(`${V2_API_BASE_URL}/login`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'X-API-KEY': credentials.apiKey,
      },
      body: new URLSearchParams({
        grant_type: 'password',
        username: credentials.username,
        password: credentials.password,
        client_id: 'vcom-api',
        client_secret: 'AYB=~9_f-BvNoLt8+x=3maCq)>/?@Nom',
      }),
    });

    if (!response.ok) {
      // console.error(`[Asset Sync] OAuth failed: ${response.status}`);
      return null;
    }

    const data = (await response.json()) as { access_token?: string };
    return data.access_token || null;
  } catch (error: any) {
    // console.error('[Asset Sync] Error getting OAuth token:', error.message);
    return null;
  }
}

/**
 * Get inverters list from V2 API
 */
async function getInvertersFromV2API(
  systemKey: string,
  credentials: { username: string; password: string; apiKey: string },
  prefetchedToken?: string | null
): Promise<Array<{ id: string; name: string; serial: string | null }> | null> {
  try {
    const token = prefetchedToken ?? (await getOAuthToken(credentials));
    if (!token) {
      // console.warn('[Asset Sync] Failed to get OAuth token, skipping V2 API inverter sync');
      return null;
    }

    const response = await fetch(`${V2_API_BASE_URL}/systems/${systemKey}/inverters`, {
      method: 'GET',
      headers: {
        'X-API-KEY': credentials.apiKey,
        'Authorization': `Bearer ${token}`,
      },
    });

    if (!response.ok) {
      // console.warn(`[Asset Sync] Failed to get inverters from V2 API: ${response.status}`);
      return null;
    }

    const data = (await response.json()) as { data?: Array<{ id: string; name?: string; serial?: string }> };
    if (data?.data && Array.isArray(data.data)) {
      return data.data.map((inv: any) => ({
        id: inv.id,
        name: inv.name || inv.id,
        serial: inv.serial || null,
      }));
    }

    return null;
  } catch (error: any) {
    // console.warn(`[Asset Sync] Error getting inverters from V2 API:`, error.message);
    return null;
  }
}

/**
 * Get detailed inverter information including serial number from V2 API
 */
async function getInverterDetailFromV2API(
  systemKey: string,
  deviceId: string,
  credentials: { username: string; password: string; apiKey: string },
  prefetchedToken?: string | null
): Promise<{ serial: string | null; model?: string; vendor?: string } | null> {
  try {
    const token = prefetchedToken ?? (await getOAuthToken(credentials));
    if (!token) {
      return null;
    }

    const response = await fetch(`${V2_API_BASE_URL}/systems/${systemKey}/inverters/${deviceId}`, {
      method: 'GET',
      headers: {
        'X-API-KEY': credentials.apiKey,
        'Authorization': `Bearer ${token}`,
      },
    });

    if (!response.ok) {
      return null;
    }

    const data = (await response.json()) as { data?: { serial?: string; model?: string; vendor?: string } };
    if (data?.data) {
      return {
        serial: data.data.serial || null,
        model: data.data.model,
        vendor: data.data.vendor,
      };
    }

    return null;
  } catch (error: any) {
    // console.warn(`[Asset Sync] Error getting inverter detail from V2 API:`, error.message);
    return null;
  }
}

/**
 * Sync Inverter Assets from Meteocontrol data
 * Now includes serial number from V2 API
 * If V2 API provides individual inverters, create separate assets for each
 */
async function syncInverterAssets(
  projectId: string,
  plantAssetId: string,
  inverters: { [model: string]: number },
  systemKey?: string,
  v2Credentials?: { username: string; password: string; apiKey: string }
): Promise<void> {
  try {
    // Get inverters from V2 API if credentials are available.
    // Fetch the OAuth token ONCE and reuse it for the list + every detail call,
    // otherwise we log in per-request and Meteocontrol rate-limits us, which makes
    // detail calls fail and forces the (buggy) empty-model fallback path.
    let v2Inverters: Array<{ id: string; name: string; serial: string | null }> | null = null;
    let v2Token: string | null = null;
    if (systemKey && v2Credentials) {
      v2Token = await getOAuthToken(v2Credentials);
      v2Inverters = await getInvertersFromV2API(systemKey, v2Credentials, v2Token);
    }

    // If we have V2 API inverters with serial numbers, create individual assets for each
    if (v2Inverters && v2Inverters.length > 0) {
      // Get detailed info for each inverter (including model, vendor)
      const inverterDetails: Array<{
        id: string;
        name: string;
        serial: string | null;
        model?: string;
        vendor?: string;
      }> = [];

      for (const inv of v2Inverters) {
        if (systemKey && v2Credentials) {
          const detail = await getInverterDetailFromV2API(systemKey, inv.id, v2Credentials, v2Token);
          if (detail) {
            inverterDetails.push({
              id: inv.id,
              name: inv.name,
              serial: detail.serial || inv.serial,
              model: detail.model,
              vendor: detail.vendor,
            });
          } else {
            inverterDetails.push({
              id: inv.id,
              name: inv.name,
              serial: inv.serial,
            });
          }
        } else {
          inverterDetails.push({
            id: inv.id,
            name: inv.name,
            serial: inv.serial,
          });
        }
      }

      // Parsed inverter models from the Widget API (e.g. "Huawei SUN2000-115KTL-M2 (400V)")
      // used to back-fill the manufacturer when the V2 detail API returns vendor=null.
      const widgetModels = Object.keys(inverters || {}).map(parseInverterModel);

      // Resolve a stable manufacturer/model for an inverter even when the V2 detail
      // API omits the vendor (it commonly returns `model` but `vendor: null`).
      const resolveModelMeta = (invDetail: { name: string; model?: string; vendor?: string }) => {
        let manufacturer = (invDetail.vendor || '').trim();
        let model = (invDetail.model || '').trim();
        // Back-fill the manufacturer from the Widget label by matching the model string.
        if (!manufacturer && model) {
          const m = widgetModels.find(
            (w) => w.model && (w.model === model || w.model.includes(model) || model.includes(w.model))
          );
          if (m?.manufacturer) manufacturer = m.manufacturer;
        }
        // If V2 gave us no model at all, fall back to parsing the device name.
        if (!model) {
          const parsed = parseInverterModel(invDetail.name);
          if (!manufacturer) manufacturer = parsed.manufacturer;
          model = parsed.model;
        }
        return { manufacturer, model };
      };

      // Group inverters by resolved model so same-model devices share one parent asset.
      const modelGroups = new Map<string, Array<{
        id: string;
        name: string;
        serial: string | null;
        model?: string;
        vendor?: string;
      }>>();

      for (const invDetail of inverterDetails) {
        const { manufacturer, model } = resolveModelMeta(invDetail);
        const modelKey =
          [manufacturer, model].filter(Boolean).join(' ').trim() ||
          invDetail.name ||
          invDetail.id;

        if (!modelGroups.has(modelKey)) {
          modelGroups.set(modelKey, []);
        }
        modelGroups.get(modelKey)!.push(invDetail);
      }

      // Track every asset we create/update so we can prune stale synced inverters
      // (e.g. duplicates left behind by an earlier buggy grouping key) afterwards.
      const touchedInverterIds = new Set<string>();

      // For each model group, create parent asset and child assets.
      // Wrap each group so one failure (e.g. a duplicate code) cannot abort the
      // whole inverter sync and silently drop the remaining inverters.
      for (const [modelKey, inverterList] of modelGroups.entries()) {
       try {
        const { manufacturer, model, capacityKW } = parseInverterModel(modelKey);
        const quantity = inverterList.length;
        const serialNumbers = inverterList.map(inv => inv.serial).filter(s => s !== null) as string[];

        // Step 1: Create or update parent grouped asset (e.g., "SOFARSOLAR 110KTL (x9)")
        let groupedAsset = await Asset.findOne({
          projectId,
          parentAssetId: plantAssetId,
          assetType: 'Inverter',
          'specifications.meteocontrolModel': modelKey,
          'specifications.isGrouped': true,
        });

        const groupedAssetName = `${manufacturer}${model ? ` ${model}` : ''} (x${quantity})`;
        // Derive the code from the full modelKey (not just `model`, which is empty
        // when the V2 detail API returns no vendor/model). Otherwise every group
        // collapses to the same `INV-GROUP-xxxxxx-` code and collides on the
        // unique { projectId, code } index, dropping all but the first inverter.
        const groupedCode = `INV-GROUP-${projectId.toString().slice(-6)}-${slugifyCode(modelKey)}`;

        if (groupedAsset) {
          groupedAsset.name = groupedAssetName;
          groupedAsset.manufacturer = manufacturer;
          groupedAsset.productModel = model;
          if (capacityKW) groupedAsset.capacityKW = capacityKW;
          if (!groupedAsset.specifications) {
            groupedAsset.specifications = {};
          }
          groupedAsset.specifications.quantity = quantity;
          groupedAsset.specifications.meteocontrolModel = modelKey;
          if (serialNumbers.length > 0) {
            groupedAsset.specifications.serialNumbers = serialNumbers;
          }
          groupedAsset.specifications.isGrouped = true;
          groupedAsset.specifications.syncedFromMeteocontrol = true;
          await groupedAsset.save();
        } else {
          groupedAsset = await Asset.create({
            projectId,
            parentAssetId: plantAssetId,
            assetType: 'Inverter',
            name: groupedAssetName,
            code: groupedCode,
            manufacturer,
            productModel: model,
            capacityKW,
            status: 'active',
            specifications: {
              quantity,
              meteocontrolModel: modelKey,
              ...(serialNumbers.length > 0 && { serialNumbers }),
              isGrouped: true,
              syncedFromMeteocontrol: true,
            },
          });
        }

        const groupedAssetId = groupedAsset._id.toString();
        touchedInverterIds.add(groupedAssetId);

        // Step 2: Create/update individual child assets (e.g., "Thành Đạt 1", "Thành Đạt 2", ...)
        for (const invDetail of inverterList) {
          const resolved = resolveModelMeta(invDetail);
          const invManufacturer = resolved.manufacturer;
          const invModel = resolved.model;
          const invCapacityKW = capacityKW; // shared model capacity for the group

          // Try to find existing child asset by V2 API inverter ID (always present),
          // and additionally by serial number when one exists. Do NOT match on
          // `serialNumber: undefined` — Mongoose strips the undefined value, leaving
          // an empty `{}` clause that matches ANY child under this group and would
          // collapse every inverter onto the first one.
          const childMatchOr: any[] = [
            { 'specifications.meteocontrolInverterId': invDetail.id },
          ];
          if (invDetail.serial) {
            childMatchOr.push({ serialNumber: invDetail.serial });
          }
          let childAsset = await Asset.findOne({
            projectId,
            parentAssetId: groupedAssetId, // Child of grouped asset
            assetType: 'Inverter',
            $or: childMatchOr,
          });

          const childAssetName = invDetail.name || `${invManufacturer} ${invModel || 'Inverter'}`;
          // Use the (unique) V2 inverter id for the code, falling back to serial.
          const childCode = `INV-${projectId.toString().slice(-6)}-${slugifyCode(invDetail.id || invDetail.serial, 32)}`;

          if (childAsset) {
            // Update existing child asset
            childAsset.name = childAssetName;
            if (invManufacturer) childAsset.manufacturer = invManufacturer;
            if (invModel) childAsset.productModel = invModel;
            if (invCapacityKW) childAsset.capacityKW = invCapacityKW;
            if (invDetail.serial) {
              childAsset.serialNumber = invDetail.serial;
            }
            childAsset.parentAssetId = groupedAssetId; // Ensure parent is correct
            if (!childAsset.specifications) {
              childAsset.specifications = {};
            }
            childAsset.specifications.meteocontrolInverterId = invDetail.id;
            childAsset.specifications.syncedFromMeteocontrol = true;
            await childAsset.save();
            touchedInverterIds.add(childAsset._id.toString());
          } else {
            // Create new child asset
            const created = await Asset.create({
              projectId,
              parentAssetId: groupedAssetId, // Child of grouped asset
              assetType: 'Inverter',
              name: childAssetName,
              code: childCode,
              manufacturer: invManufacturer,
              productModel: invModel,
              capacityKW: invCapacityKW,
              serialNumber: invDetail.serial || undefined,
              status: 'active',
              specifications: {
                meteocontrolInverterId: invDetail.id,
                syncedFromMeteocontrol: true,
              },
            });
            touchedInverterIds.add(created._id.toString());
          }
        }
       } catch (groupError: any) {
         // Isolate per-group failures so the remaining inverter groups still sync.
         // console.error(`[Asset Sync] Error syncing inverter group "${modelKey}":`, groupError.message);
       }
      }

      // Prune stale inverter assets left over from earlier syncs (e.g. duplicates
      // created under a previous, buggy grouping key). Only remove assets that were
      // previously synced from Meteocontrol under THIS plant and were NOT touched in
      // this run — manually-created inverters (no syncedFromMeteocontrol flag) are
      // never deleted.
      if (touchedInverterIds.size > 0) {
        try {
          const synced = await Asset.find({
            projectId,
            assetType: 'Inverter',
            'specifications.syncedFromMeteocontrol': true,
          }).select('_id');

          const staleIds = synced
            .filter((a) => !touchedInverterIds.has(a._id.toString()))
            .map((a) => a._id);

          if (staleIds.length > 0) {
            await Asset.deleteMany({ _id: { $in: staleIds } });
            // console.log(`[Asset Sync] Pruned ${staleIds.length} stale inverter asset(s) for project ${projectId}`);
          }
        } catch (pruneError: any) {
          // console.warn('[Asset Sync] Failed to prune stale inverter assets:', pruneError.message);
        }
      }

      return; // Exit early if we processed V2 API inverters
    }

    // Fallback: Original logic for Widget API only (no V2 API or no inverters from V2 API)
    for (const [modelName, quantity] of Object.entries(inverters)) {
      const { manufacturer, model, capacityKW } = parseInverterModel(modelName);

      // Find existing Inverter Asset with same model
      let inverterAsset = await Asset.findOne({
        projectId,
        parentAssetId: plantAssetId,
        assetType: 'Inverter',
        'specifications.meteocontrolModel': modelName,
      });

      const assetName = `${manufacturer} ${model}${quantity > 1 ? ` (x${quantity})` : ''}`;
      const code = `INV-${projectId.toString().slice(-6)}-${slugifyCode(modelName)}`;

      if (inverterAsset) {
        // Update existing
        inverterAsset.name = assetName;
        inverterAsset.manufacturer = manufacturer;
        inverterAsset.productModel = model;
        inverterAsset.capacityKW = capacityKW;
        if (!inverterAsset.specifications) {
          inverterAsset.specifications = {};
        }
        inverterAsset.specifications.quantity = quantity;
        inverterAsset.specifications.meteocontrolModel = modelName;
        inverterAsset.specifications.syncedFromMeteocontrol = true;
        await inverterAsset.save();
      } else {
        // Create new
        await Asset.create({
          projectId,
          parentAssetId: plantAssetId,
          assetType: 'Inverter',
          name: assetName,
          code,
          manufacturer,
          productModel: model,
          capacityKW,
          status: 'active',
          specifications: {
            quantity,
            meteocontrolModel: modelName,
            syncedFromMeteocontrol: true,
          },
        });
      }
    }
  } catch (error: any) {
    // console.error(`[Asset Sync] Error syncing Inverter Assets:`, error.message);
    throw error;
  }
}

/**
 * Sync Panel Assets from Meteocontrol data
 */
async function syncPanelAssets(
  projectId: string,
  plantAssetId: string,
  modules: { [model: string]: number }
): Promise<void> {
  try {
    for (const [modelName, quantity] of Object.entries(modules)) {
      const { manufacturer, model, capacityW } = parsePanelModel(modelName);

      // Find existing Panel Asset with same model
      let panelAsset = await Asset.findOne({
        projectId,
        parentAssetId: plantAssetId,
        assetType: 'Panel',
        'specifications.meteocontrolModel': modelName,
      });

      const assetName = `${manufacturer} ${model}${quantity > 1 ? ` (x${quantity})` : ''}`;
      const code = `PANEL-${projectId.toString().slice(-6)}-${slugifyCode(modelName)}`;

      if (panelAsset) {
        // Update existing
        panelAsset.name = assetName;
        panelAsset.manufacturer = manufacturer;
        panelAsset.productModel = model;
        if (!panelAsset.specifications) {
          panelAsset.specifications = {};
        }
        panelAsset.specifications.quantity = quantity;
        if (capacityW) {
          panelAsset.specifications.capacityW = capacityW;
        }
        panelAsset.specifications.meteocontrolModel = modelName;
        panelAsset.specifications.syncedFromMeteocontrol = true;
        await panelAsset.save();
      } else {
        // Create new
        await Asset.create({
          projectId,
          parentAssetId: plantAssetId,
          assetType: 'Panel',
          name: assetName,
          code,
          manufacturer,
          productModel: model,
          status: 'active',
          specifications: {
            quantity,
            capacityW,
            meteocontrolModel: modelName,
            syncedFromMeteocontrol: true,
          },
        });
      }
    }
  } catch (error: any) {
    // console.error(`[Asset Sync] Error syncing Panel Assets:`, error.message);
    throw error;
  }
}

/**
 * Update Project information from Meteocontrol System Information
 */
async function updateProjectFromSystemInfo(
  projectId: string,
  siteData: SystemInformation['siteDataCollection'][string]
): Promise<void> {
  try {
    const project = await Project.findById(projectId);
    if (!project) {
      // console.error(`[Asset Sync] Project not found for update: ${projectId}`);
      return;
    }

    let hasUpdates = false;
    const updates: any = {};

    // Update capacity (nominalDCOutput in kWp -> capacityMWp in MWp)
    if (siteData.nominalDCOutput && siteData.nominalDCOutput > 0) {
      const capacityMWp = siteData.nominalDCOutput / 1000; // Convert kWp to MWp
      if (Math.abs((project.capacityMWp || 0) - capacityMWp) > 0.001) {
        updates.capacityMWp = capacityMWp;
        hasUpdates = true;
        // console.log(`[Asset Sync] Updating capacity for project ${projectId}: ${project.capacityMWp} MWp -> ${capacityMWp} MWp`);
      }
    }

    // Update location (coordinates and address)
    if (siteData.location) {
      const locationUpdates: any = {};

      // Update coordinates
      if (siteData.location.latitude && siteData.location.longitude) {
        const currentLat = project.location?.coordinates?.lat;
        const currentLng = project.location?.coordinates?.lng;
        
        if (currentLat !== siteData.location.latitude || currentLng !== siteData.location.longitude) {
          locationUpdates.coordinates = {
            lat: siteData.location.latitude,
            lng: siteData.location.longitude,
          };
          hasUpdates = true;
          // console.log(`[Asset Sync] Updating coordinates for project ${projectId}`);
        }
      }

      // Update address (combine street, city, country)
      if (siteData.location.street || siteData.location.city || siteData.location.country) {
        const addressParts: string[] = [];
        if (siteData.location.street) addressParts.push(siteData.location.street);
        if (siteData.location.city) addressParts.push(siteData.location.city);
        if (siteData.location.country) addressParts.push(siteData.location.country);
        
        const newAddress = addressParts.join(', ');
        if (project.location?.address !== newAddress && newAddress.trim()) {
          locationUpdates.address = newAddress;
          hasUpdates = true;
          // console.log(`[Asset Sync] Updating address for project ${projectId}`);
        }
      }

      if (Object.keys(locationUpdates).length > 0) {
        updates.location = {
          ...project.location,
          ...locationUpdates,
        };
      }
    }

    // Update commissioning date (startupDate)
    if (siteData.startupDate) {
      const newCommissioningDate = parseGermanDate(siteData.startupDate);
      if (newCommissioningDate) {
        const currentDate = project.commissioningDate ? new Date(project.commissioningDate) : null;
        const newDateStr = newCommissioningDate.toISOString().split('T')[0];
        const currentDateStr = currentDate ? currentDate.toISOString().split('T')[0] : null;
        
        if (currentDateStr !== newDateStr) {
          updates.commissioningDate = newCommissioningDate;
          hasUpdates = true;
          // console.log(`[Asset Sync] Updating commissioning date for project ${projectId}: ${currentDateStr} -> ${newDateStr}`);
        }
      }
    }

    // Update project name (siteName) if available and different
    if (siteData.siteName && siteData.siteName.trim() && project.name !== siteData.siteName.trim()) {
      updates.name = siteData.siteName.trim();
      hasUpdates = true;
      // console.log(`[Asset Sync] Updating project name for project ${projectId}: "${project.name}" -> "${siteData.siteName.trim()}"`);
    }

    // Apply updates if any
    if (hasUpdates) {
      await Project.findByIdAndUpdate(projectId, updates, { new: true });
      // console.log(`[Asset Sync] Successfully updated project ${projectId} information from Meteocontrol`);
    } else {
      // console.log(`[Asset Sync] No updates needed for project ${projectId}`);
    }
  } catch (error: any) {
    // console.error(`[Asset Sync] Error updating project ${projectId} from system info:`, error.message);
    // Don't throw - allow asset sync to continue even if project update fails
  }
}

/**
 * Sync assets for a single project
 */
export async function syncAssetsForProject(projectId: string): Promise<boolean> {
  try {
    // Get project
    const project = await Project.findById(projectId);
    if (!project) {
      // console.error(`[Asset Sync] Project not found: ${projectId}`);
      return false;
    }

    // Check if project has Meteocontrol config
    if (!project.meteocontrol?.siteKey || !project.meteocontrol?.apiKey) {
      // console.log(`[Asset Sync] Project ${projectId} does not have Meteocontrol config, skipping`);
      return false;
    }

    const { siteKey, apiKey, systemKey } = project.meteocontrol;

    // Get V2 API credentials from SystemSettings (for serial number sync)
    let v2Credentials: { username: string; password: string; apiKey: string } | undefined;
    try {
      const settings = await SystemSettings.findOne();
      if (settings?.vcomApi?.username && settings?.vcomApi?.password && settings?.vcomApi?.apiKey) {
        v2Credentials = {
          username: settings.vcomApi.username,
          password: settings.vcomApi.password,
          apiKey: settings.vcomApi.apiKey,
        };
      }
    } catch (error: any) {
      // console.warn(`[Asset Sync] Failed to get V2 API credentials, will skip serial number sync:`, error.message);
    }

    // Get System Information from Meteocontrol
    const systemInfo = await getSystemInformation(siteKey, apiKey);
    if (!systemInfo?.siteDataCollection?.[siteKey]) {
      // console.error(`[Asset Sync] Failed to get system information for site ${siteKey}`);
      return false;
    }

    const siteData = systemInfo.siteDataCollection[siteKey];

    // Update Project information from System Information (don't fail if this fails)
    try {
      await updateProjectFromSystemInfo(projectId, siteData);
    } catch (updateError: any) {
      // console.warn(`[Asset Sync] Failed to update project info, continuing with asset sync:`, updateError.message);
    }

    // Find or create Plant Asset
    const plantAssetId = await findOrCreatePlantAsset(projectId, siteData);
    if (!plantAssetId) {
      // console.error(`[Asset Sync] Failed to create Plant Asset for project ${projectId}`);
      return false;
    }

    // Sync Inverter Assets (don't fail if this fails)
    // Now includes serial number from V2 API if credentials are available
    if (siteData.inverters && Object.keys(siteData.inverters).length > 0) {
      try {
        const effectiveSystemKey = systemKey || siteKey;
        await syncInverterAssets(projectId, plantAssetId, siteData.inverters, effectiveSystemKey, v2Credentials);
        // console.log(`[Asset Sync] Synced ${Object.keys(siteData.inverters).length} inverter model(s) for project ${projectId}`);
      } catch (inverterError: any) {
        // console.error(`[Asset Sync] Error syncing inverters, continuing:`, inverterError.message);
      }
    }

    // Sync Panel Assets (don't fail if this fails)
    if (siteData.modules && Object.keys(siteData.modules).length > 0) {
      try {
        await syncPanelAssets(projectId, plantAssetId, siteData.modules);
        // console.log(`[Asset Sync] Synced ${Object.keys(siteData.modules).length} panel model(s) for project ${projectId}`);
      } catch (panelError: any) {
        // console.error(`[Asset Sync] Error syncing panels, continuing:`, panelError.message);
      }
    }

    // console.log(`[Asset Sync] Successfully synced assets for project ${projectId}`);
    return true;
  } catch (error: any) {
    // console.error(`[Asset Sync] Error syncing assets for project ${projectId}:`, error.message);
    // console.error(`[Asset Sync] Error stack:`, error.stack);
    return false;
  }
}

/**
 * Sync assets for all projects with Meteocontrol config
 */
export async function syncAssetsForAllProjects(): Promise<void> {
  try {
    // console.log('[Asset Sync] Starting asset synchronization for all projects');

    // Get all projects with Meteocontrol config
    const projects = await Project.find({
      'meteocontrol.siteKey': { $exists: true, $ne: '' },
      'meteocontrol.apiKey': { $exists: true, $ne: '' },
    });

    // console.log(`[Asset Sync] Found ${projects.length} projects with Meteocontrol config`);

    let successCount = 0;
    let failCount = 0;

    for (const project of projects) {
      const success = await syncAssetsForProject(project._id.toString());
      if (success) {
        successCount++;
      } else {
        failCount++;
      }

      // Add small delay to respect rate limits
      await new Promise(resolve => setTimeout(resolve, 500)); // 0.5 second delay
    }

    // console.log(`[Asset Sync] Completed: ${successCount} success, ${failCount} failed`);
  } catch (error: any) {
    // console.error('[Asset Sync] Error in syncAssetsForAllProjects:', error.message);
    throw error;
  }
}
