import { ServerAPI } from '@signalk/server-api';
import { VesselContext, VesselInfo, VesselContextExtraction } from './types';
import * as fs from 'fs-extra';
import * as path from 'path';

/**
 * Vessel Context Manager - Extracts vessel information from SignalK data
 * and manages vessel context document for Claude AI analysis
 */
export class VesselContextManager {
  private app?: ServerAPI;
  private contextFilePath: string;
  private vesselContext?: VesselContext;

  // SignalK paths to extract vessel information from
  private static readonly VESSEL_DATA_PATHS: VesselContextExtraction[] = [
    // Basic identification
    {
      path: 'name',
      signalkPath: 'name',
      displayName: 'Vessel Name',
      category: 'identification',
    },
    {
      path: 'callsign',
      signalkPath: 'communication.callsignVhf',
      displayName: 'Call Sign',
      category: 'identification',
    },
    {
      path: 'mmsi',
      signalkPath: 'mmsi',
      displayName: 'MMSI',
      category: 'identification',
    },

    // Physical characteristics
    {
      path: 'length',
      signalkPath: 'design.length',
      displayName: 'Length Overall (LOA)',
      unit: 'm',
      category: 'physical',
    },
    {
      path: 'beam',
      signalkPath: 'design.beam',
      displayName: 'Beam',
      unit: 'm',
      category: 'physical',
    },
    {
      path: 'draft',
      signalkPath: 'design.draft',
      displayName: 'Maximum Draft',
      unit: 'm',
      category: 'physical',
    },
    {
      path: 'height',
      signalkPath: 'design.airHeight',
      displayName: 'Air Draft/Height',
      unit: 'm',
      category: 'physical',
    },
    {
      path: 'displacement',
      signalkPath: 'design.displacement',
      displayName: 'Displacement',
      unit: 'kg',
      category: 'physical',
    },

    // Vessel classification
    {
      path: 'vesselType',
      signalkPath: 'design.aisShipAndCargoType',
      displayName: 'Vessel Type',
      category: 'classification',
    },
    {
      path: 'flag',
      signalkPath: 'registrations.imo.country',
      displayName: 'Flag State',
      category: 'classification',
    },

    // Technical specifications
    {
      path: 'grossTonnage',
      signalkPath: 'registrations.imo.grossTonnage',
      displayName: 'Gross Tonnage',
      unit: 'GT',
      category: 'technical',
    },
    {
      path: 'netTonnage',
      signalkPath: 'registrations.imo.netTonnage',
      displayName: 'Net Tonnage',
      unit: 'NT',
      category: 'technical',
    },
    {
      path: 'deadWeight',
      signalkPath: 'design.deadweight',
      displayName: 'Deadweight',
      unit: 'tonnes',
      category: 'technical',
    },

    // Build information
    {
      path: 'builder',
      signalkPath: 'design.constructor',
      displayName: 'Builder/Constructor',
      category: 'build',
    },
    {
      path: 'buildYear',
      signalkPath: 'design.construction.year',
      displayName: 'Build Year',
      category: 'build',
    },
    {
      path: 'hullNumber',
      signalkPath: 'registrations.other.hullNumber',
      displayName: 'Hull Number',
      category: 'build',
    },

    // Contact information
    {
      path: 'ownerName',
      signalkPath: 'registrations.imo.owner',
      displayName: 'Owner',
      category: 'contact',
    },
    {
      path: 'port',
      signalkPath: 'port',
      displayName: 'Port of Registry',
      category: 'contact',
    },
  ];

  constructor(app?: ServerAPI, dataDirectory?: string) {
    this.app = app;

    // Must have a data directory - this should be the plugin's configured directory
    if (!dataDirectory) {
      throw new Error('Data directory is required for vessel context manager');
    }
    this.contextFilePath = path.join(dataDirectory, 'vessel-context.json');

    this.app?.debug(`Vessel context file path: ${this.contextFilePath}`);

    this.loadVesselContext();
  }

  /**
   * Extract vessel information from SignalK data
   */
  async extractVesselInfo(): Promise<VesselInfo> {
    const vesselInfo: VesselInfo = {};

    if (!this.app) {
      console.debug('No SignalK app available for vessel data extraction');
      return vesselInfo;
    }

    try {
      // Get vessel context (defaults to vessels.self)
      // Cast to any for compatibility with different @signalk/server-api versions
      const vesselContext = (this.app.getSelfPath('') as any) || 'vessels.self';

      this.app.debug(`Extracting vessel info from context: ${vesselContext}`);

      // Extract data for each defined path
      for (const extraction of VesselContextManager.VESSEL_DATA_PATHS) {
        try {
          // Get value from SignalK path
          // Cast to any for compatibility with different @signalk/server-api versions
          const value = this.app.getSelfPath(extraction.signalkPath) as any;

          if (value !== null && value !== undefined) {
            // Convert units and handle different data types
            let processedValue = value;

            // Handle specific field processing
            if (extraction.path === 'vesselType') {
              // Handle AIS ship type - could be number or object
              if (typeof value === 'number') {
                processedValue = this.convertAISShipType(value);
                vesselInfo.vesselType = processedValue;
              } else if (typeof value === 'object' && value !== null) {
                if (value.name) {
                  processedValue = value.name;
                  vesselInfo.vesselType = processedValue;
                } else if (value.id !== undefined) {
                  processedValue = this.convertAISShipType(value.id);
                  vesselInfo.vesselType = processedValue;
                }
              } else if (typeof value === 'string') {
                vesselInfo.vesselType = value;
              }
            } else if (
              extraction.path === 'displacement' &&
              extraction.unit === 'm'
            ) {
              // Convert kg to tonnes for displacement
              if (typeof value === 'number') {
                processedValue = value / 1000;
                vesselInfo.displacement = processedValue;
              }
            } else {
              // Handle simple numbers and strings
              if (typeof value === 'number' || typeof value === 'string') {
                (vesselInfo as any)[extraction.path] = value;
                processedValue = value;
              } else if (typeof value === 'object' && value !== null) {
                // Handle nested objects - try to extract the actual value
                let extractedValue = null;

                // Common patterns in SignalK data
                if (value.overall !== undefined) {
                  extractedValue = value.overall;
                } else if (value.maximum !== undefined) {
                  extractedValue = value.maximum;
                } else if (value.minimum !== undefined) {
                  extractedValue = value.minimum;
                } else if (value.hull !== undefined) {
                  extractedValue = value.hull;
                } else if (value.value !== undefined) {
                  // Handle nested value objects (like {"value":{"overall":16}})
                  if (typeof value.value === 'object' && value.value !== null) {
                    if (value.value.overall !== undefined) {
                      extractedValue = value.value.overall;
                    } else if (value.value.maximum !== undefined) {
                      extractedValue = value.value.maximum;
                    } else if (value.value.minimum !== undefined) {
                      extractedValue = value.value.minimum;
                    } else {
                      // Take first available value from nested object
                      const nestedKeys = Object.keys(value.value);
                      if (nestedKeys.length > 0) {
                        extractedValue = value.value[nestedKeys[0]];
                      }
                    }
                  } else {
                    extractedValue = value.value;
                  }
                } else {
                  // If it's a simple object with one key, try to extract that value
                  const keys = Object.keys(value);
                  if (keys.length === 1) {
                    extractedValue = value[keys[0]];
                  } else if (keys.length > 1) {
                    // Take the second value if multiple keys exist
                    extractedValue = value[keys[1]];
                  }
                }

                if (extractedValue !== null && extractedValue !== undefined) {
                  (vesselInfo as any)[extraction.path] = extractedValue;
                  processedValue = extractedValue;
                }
              }
            }

            this.app.debug(
              `Extracted ${extraction.displayName}: ${processedValue}`
            );
          }
        } catch (error) {
          this.app?.debug(
            `Failed to extract ${extraction.displayName}: ${(error as Error).message}`
          );
        }
      }

      // Try to extract additional vessel data from other common paths
      await this.extractAdditionalVesselData(vesselInfo);
    } catch (error) {
      this.app?.error(
        `Failed to extract vessel information: ${(error as Error).message}`
      );
    }

    return vesselInfo;
  }

  /**
   * Extract additional vessel data from other SignalK paths
   */
  private async extractAdditionalVesselData(
    vesselInfo: VesselInfo
  ): Promise<void> {
    if (!this.app) return;

    try {
      // Try alternative paths for common data
      // Cast to any for compatibility with different @signalk/server-api versions
      if (!vesselInfo.name) {
        const altName =
          (this.app.getSelfPath(
            'registrations.national.registration'
          ) as any) ||
          (this.app.getSelfPath('registrations.local.registration') as any);
        if (altName) {
          vesselInfo.name = altName;
        }
      }

      if (!vesselInfo.length) {
        // Try alternative length paths
        let altLength =
          (this.app.getSelfPath('design.length.hull') as any) ||
          (this.app.getSelfPath('design.length.waterline') as any);

        // If we get the whole length object, try to extract a useful value
        if (!altLength) {
          const lengthObj = this.app.getSelfPath('design.length') as any;
          if (lengthObj && typeof lengthObj === 'object') {
            altLength =
              lengthObj.overall || lengthObj.hull || lengthObj.waterline;
          }
        }

        if (altLength && typeof altLength === 'number') {
          vesselInfo.length = altLength;
        }
      }

      if (!vesselInfo.draft) {
        // Try alternative draft paths
        let altDraft =
          (this.app.getSelfPath('design.draft.minimum') as any) ||
          (this.app.getSelfPath('design.draft.current') as any);

        // If we get the whole draft object, try to extract a useful value
        if (!altDraft) {
          const draftObj = this.app.getSelfPath('design.draft') as any;
          if (draftObj && typeof draftObj === 'object') {
            altDraft = draftObj.maximum || draftObj.minimum || draftObj.current;
          }
        }

        if (altDraft && typeof altDraft === 'number') {
          vesselInfo.draft = altDraft;
        }
      }

      // Extract additional notes from description fields
      const description = this.app.getSelfPath('design.description') as any;
      if (description && !vesselInfo.notes) {
        vesselInfo.notes = description;
      }
    } catch (error) {
      this.app?.debug(
        `Failed to extract additional vessel data: ${(error as Error).message}`
      );
    }
  }

  /**
   * Convert AIS ship type number to readable string
   */
  private convertAISShipType(shipType: number): string {
    const aisTypes: { [key: number]: string } = {
      36: 'Sailing vessel',
      37: 'Pleasure craft',
      31: 'Towing vessel',
      32: 'Towing vessel (length > 200m)',
      33: 'Vessel engaged in dredging',
      34: 'Vessel engaged in diving operations',
      35: 'Military vessel',
      30: 'Fishing vessel',
      20: 'Wing in ground craft',
      21: 'Cargo vessel',
      22: 'Cargo vessel',
      23: 'Cargo vessel',
      24: 'Cargo vessel',
      25: 'Cargo vessel',
      26: 'Cargo vessel',
      27: 'Cargo vessel',
      28: 'Cargo vessel',
      29: 'Cargo vessel',
      70: 'Passenger vessel',
      71: 'Passenger vessel',
      72: 'Passenger vessel',
      73: 'Passenger vessel',
      74: 'Passenger vessel',
      75: 'Passenger vessel',
      76: 'Passenger vessel',
      77: 'Passenger vessel',
      78: 'Passenger vessel',
      79: 'Passenger vessel',
      80: 'Tanker',
      81: 'Tanker',
      82: 'Tanker',
      83: 'Tanker',
      84: 'Tanker',
      85: 'Tanker',
      86: 'Tanker',
      87: 'Tanker',
      88: 'Tanker',
      89: 'Tanker',
    };

    return aisTypes[shipType] || `Unknown vessel type (${shipType})`;
  }

  /**
   * Load vessel context from file
   */
  private async loadVesselContext(): Promise<void> {
    try {
      if (await fs.pathExists(this.contextFilePath)) {
        this.vesselContext = await fs.readJson(this.contextFilePath);
        this.app?.debug(`Loaded vessel context from ${this.contextFilePath}`);
      } else {
        // Create default context
        this.vesselContext = {
          vesselInfo: {},
          customContext: '',
          lastUpdated: new Date().toISOString(),
          autoExtracted: false,
        };
        await this.saveVesselContext();
      }
    } catch (error) {
      this.app?.error(
        `Failed to load vessel context: ${(error as Error).message}`
      );
      // Create default context on error
      this.vesselContext = {
        vesselInfo: {},
        customContext: '',
        lastUpdated: new Date().toISOString(),
        autoExtracted: false,
      };
    }
  }

  /**
   * Save vessel context to file
   */
  async saveVesselContext(): Promise<void> {
    try {
      if (!this.vesselContext) return;

      // Ensure directory exists
      await fs.ensureDir(path.dirname(this.contextFilePath));

      // Update last modified time
      this.vesselContext.lastUpdated = new Date().toISOString();

      // Save to file
      await fs.writeJson(this.contextFilePath, this.vesselContext, {
        spaces: 2,
      });

      this.app?.debug(`Saved vessel context to ${this.contextFilePath}`);
    } catch (error) {
      this.app?.error(
        `Failed to save vessel context: ${(error as Error).message}`
      );
      throw error;
    }
  }

  /**
   * Get current vessel context - ensure it's loaded first
   */
  async getVesselContext(): Promise<VesselContext | undefined> {
    // Ensure context is loaded
    if (!this.vesselContext) {
      await this.loadVesselContext();
    }
    return this.vesselContext;
  }

  /**
   * Update vessel context with new information
   */
  async updateVesselContext(
    vesselInfo?: Partial<VesselInfo>,
    customContext?: string,
    autoExtracted: boolean = false
  ): Promise<VesselContext> {
    // Ensure context is loaded first
    if (!this.vesselContext) {
      await this.loadVesselContext();
    }

    // If still no context after loading, create a default one
    if (!this.vesselContext) {
      this.vesselContext = {
        vesselInfo: {},
        customContext: '',
        lastUpdated: new Date().toISOString(),
        autoExtracted: false,
      };
    }

    // Update vessel info if provided
    if (vesselInfo) {
      this.vesselContext.vesselInfo = {
        ...this.vesselContext.vesselInfo,
        ...vesselInfo,
      };
    }

    // Update custom context if provided
    if (customContext !== undefined) {
      this.vesselContext.customContext = customContext;
    }

    // Set auto-extracted flag
    this.vesselContext.autoExtracted = autoExtracted;

    // Save changes
    await this.saveVesselContext();

    return this.vesselContext;
  }

  /**
   * Refresh vessel information from SignalK
   */
  async refreshVesselInfo(): Promise<VesselContext> {
    const extractedInfo = await this.extractVesselInfo();
    return await this.updateVesselContext(extractedInfo, undefined, true);
  }

  /**
   * Generate context string for Claude AI
   */
  generateClaudeContext(): string {
    if (!this.vesselContext) {
      return '=== VESSEL CONTEXT ===\n\nNo vessel context information available.\n\n=== END VESSEL CONTEXT ===\n';
    }

    const { vesselInfo, customContext } = this.vesselContext;
    const contextParts: string[] = [];

    // Add vessel identification
    contextParts.push('=== VESSEL CONTEXT ===');

    if (vesselInfo.name || vesselInfo.callsign || vesselInfo.mmsi) {
      contextParts.push('\n--- VESSEL IDENTIFICATION ---');
      if (vesselInfo.name) contextParts.push(`Vessel Name: ${vesselInfo.name}`);
      if (vesselInfo.callsign)
        contextParts.push(`Call Sign: ${vesselInfo.callsign}`);
      if (vesselInfo.mmsi) contextParts.push(`MMSI: ${vesselInfo.mmsi}`);
      if (vesselInfo.flag) contextParts.push(`Flag: ${vesselInfo.flag}`);
      if (vesselInfo.port)
        contextParts.push(`Port of Registry: ${vesselInfo.port}`);
    }

    // Add physical characteristics
    if (
      vesselInfo.length ||
      vesselInfo.beam ||
      vesselInfo.draft ||
      vesselInfo.height ||
      vesselInfo.displacement
    ) {
      contextParts.push('\n--- PHYSICAL CHARACTERISTICS ---');
      if (vesselInfo.length)
        contextParts.push(`Length Overall (LOA): ${vesselInfo.length}m`);
      if (vesselInfo.beam) contextParts.push(`Beam: ${vesselInfo.beam}m`);
      if (vesselInfo.draft) contextParts.push(`Draft: ${vesselInfo.draft}m`);
      if (vesselInfo.height)
        contextParts.push(`Air Draft/Height: ${vesselInfo.height}m`);
      if (vesselInfo.displacement)
        contextParts.push(`Displacement: ${vesselInfo.displacement} tonnes`);
    }

    // Add vessel type and classification
    if (vesselInfo.vesselType || vesselInfo.classification) {
      contextParts.push('\n--- VESSEL TYPE & CLASSIFICATION ---');
      if (vesselInfo.vesselType)
        contextParts.push(`Vessel Type: ${vesselInfo.vesselType}`);
      if (vesselInfo.classification)
        contextParts.push(`Classification: ${vesselInfo.classification}`);
    }

    // Add technical specifications
    if (
      vesselInfo.grossTonnage ||
      vesselInfo.netTonnage ||
      vesselInfo.deadWeight
    ) {
      contextParts.push('\n--- TECHNICAL SPECIFICATIONS ---');
      if (vesselInfo.grossTonnage)
        contextParts.push(`Gross Tonnage: ${vesselInfo.grossTonnage} GT`);
      if (vesselInfo.netTonnage)
        contextParts.push(`Net Tonnage: ${vesselInfo.netTonnage} NT`);
      if (vesselInfo.deadWeight)
        contextParts.push(`Deadweight: ${vesselInfo.deadWeight} tonnes`);
    }

    // Add build information
    if (vesselInfo.builder || vesselInfo.buildYear || vesselInfo.hullNumber) {
      contextParts.push('\n--- BUILD INFORMATION ---');
      if (vesselInfo.builder)
        contextParts.push(`Builder: ${vesselInfo.builder}`);
      if (vesselInfo.buildYear)
        contextParts.push(`Build Year: ${vesselInfo.buildYear}`);
      if (vesselInfo.hullNumber)
        contextParts.push(`Hull Number: ${vesselInfo.hullNumber}`);
    }

    // Add contact information
    if (vesselInfo.ownerName) {
      contextParts.push('\n--- CONTACT INFORMATION ---');
      contextParts.push(`Owner: ${vesselInfo.ownerName}`);
    }

    // Add additional notes
    if (vesselInfo.notes) {
      contextParts.push('\n--- ADDITIONAL VESSEL NOTES ---');
      contextParts.push(vesselInfo.notes);
    }

    // Add custom context
    if (customContext && customContext.trim()) {
      contextParts.push('\n--- CUSTOM OPERATIONAL CONTEXT ---');
      contextParts.push(customContext.trim());
    }

    contextParts.push('\n=== END VESSEL CONTEXT ===\n');

    return contextParts.join('\n');
  }

  /**
   * Get available vessel data paths for UI
   */
  static getVesselDataPaths(): VesselContextExtraction[] {
    return [...VesselContextManager.VESSEL_DATA_PATHS];
  }
}
