/**
 * Pulse Grow API Service
 * 
 * Server-side integration with Pulse Grow sensor platform.
 * API Docs: https://api.pulsegrow.com/docs
 */

const PULSE_API_BASE = 'https://api.pulsegrow.com';

export interface PulseDevice {
    id: string;
    name: string;
    type: string;
    isOnline: boolean;
}

export interface PulseReading {
    deviceId: string;
    deviceName: string;
    temperature: number;      // Fahrenheit
    humidity: number;         // %
    vpd: number;              // kPa
    dewpoint: number;         // Fahrenheit
    light?: number;           // lux
    co2?: number;             // ppm
    timestamp: Date;
}

export class PulseService {
    private apiKey: string;

    constructor(apiKey: string) {
        this.apiKey = apiKey;
    }

    private async fetch(path: string): Promise<any> {
        const res = await fetch(`${PULSE_API_BASE}${path}`, {
            headers: {
                'x-api-key': this.apiKey,
                'Content-Type': 'application/json',
            },
        });

        if (!res.ok) {
            const text = await res.text();
            throw new Error(`Pulse API error ${res.status}: ${text}`);
        }

        return res.json();
    }

    /**
     * Get all devices with their latest readings
     * Uses /all-devices which returns deviceViewDtos with mostRecentDataPoint
     */
    async getAllDevicesWithReadings(): Promise<{ devices: PulseDevice[]; readings: PulseReading[] }> {
        const data = await this.fetch('/all-devices');

        const devices: PulseDevice[] = [];
        const readings: PulseReading[] = [];

        // Process device view DTOs
        for (const d of (data.deviceViewDtos || [])) {
            const deviceId = String(d.id || d.deviceId);
            const deviceName = d.name || 'Unknown';

            devices.push({
                id: deviceId,
                name: deviceName,
                type: this.getDeviceType(d.deviceType),
                isOnline: d.mostRecentDataPoint?.pluggedIn ?? true,
            });

            if (d.mostRecentDataPoint) {
                const dp = d.mostRecentDataPoint;
                readings.push({
                    deviceId,
                    deviceName,
                    temperature: dp.temperatureF ?? 0,
                    humidity: dp.humidityRh ?? 0,
                    vpd: dp.vpd ?? 0,
                    dewpoint: dp.dpF ?? this.calculateDewpoint(dp.temperatureF, dp.humidityRh),
                    light: dp.lightLux || undefined,
                    co2: dp.co2 || undefined,
                    timestamp: new Date(dp.createdAt || Date.now()),
                });
            }
        }

        // Process universal sensor views if present
        for (const s of (data.universalSensorViews || [])) {
            const deviceId = String(s.id || s.deviceId);
            const deviceName = s.name || 'Unknown Sensor';

            devices.push({
                id: deviceId,
                name: deviceName,
                type: 'universal',
                isOnline: true,
            });
        }

        return { devices, readings };
    }

    /**
     * Get all devices for this grow
     */
    async getDevices(): Promise<PulseDevice[]> {
        const { devices } = await this.getAllDevicesWithReadings();
        return devices;
    }

    /**
     * Get current readings for all devices
     */
    async getCurrentReadings(): Promise<PulseReading[]> {
        const { readings } = await this.getAllDevicesWithReadings();
        return readings;
    }

    /**
     * Get current reading for a specific device
     */
    async getDeviceReading(deviceId: string): Promise<Omit<PulseReading, 'deviceId' | 'deviceName'> | null> {
        try {
            const data = await this.fetch(`/devices/${deviceId}/recent-data`);

            return {
                temperature: data.temperatureF ?? 0,
                humidity: data.humidityRh ?? 0,
                vpd: data.vpd ?? 0,
                dewpoint: data.dpF ?? this.calculateDewpoint(data.temperatureF, data.humidityRh),
                light: data.lightLux || undefined,
                co2: data.co2 || undefined,
                timestamp: new Date(data.createdAt || Date.now()),
            };
        } catch {
            return null;
        }
    }

    /**
     * Get historical data for a device
     */
    async getHistory(deviceId: string, hours: number = 24): Promise<PulseReading[]> {
        const start = new Date(Date.now() - hours * 60 * 60 * 1000).toISOString();
        const data = await this.fetch(`/devices/${deviceId}/data-range?start=${start}`);

        return (data || []).map((r: any) => ({
            deviceId,
            deviceName: '',
            temperature: r.temperatureF ?? 0,
            humidity: r.humidityRh ?? 0,
            vpd: r.vpd ?? 0,
            dewpoint: r.dpF ?? this.calculateDewpoint(r.temperatureF, r.humidityRh),
            light: r.lightLux || undefined,
            co2: r.co2 || undefined,
            timestamp: new Date(r.createdAt),
        }));
    }

    /**
     * Get sparkline data (last 1 hour) for all devices
     */
    async getSparklines(): Promise<Record<string, PulseReading[]>> {
        const devices = await this.getDevices();
        const results: Record<string, PulseReading[]> = {};

        await Promise.all(devices.map(async (device) => {
            try {
                // Get 24 hours of history
                const history = await this.getHistory(device.id, 24);
                // Sort by timestamp ascending
                results[device.id] = history.sort((a, b) =>
                    a.timestamp.getTime() - b.timestamp.getTime()
                );
            } catch (error) {
                console.warn(`Failed to fetch history for sparkline (device ${device.id})`, error);
                results[device.id] = [];
            }
        }));

        return results;
    }

    /**
     * Test connection to Pulse API
     */
    async testConnection(): Promise<{ success: boolean; deviceCount: number; error?: string }> {
        try {
            const { devices } = await this.getAllDevicesWithReadings();
            return { success: true, deviceCount: devices.length };
        } catch (error: any) {
            return { success: false, deviceCount: 0, error: error.message };
        }
    }

    /**
     * Map device type number to string
     */
    private getDeviceType(type: number): string {
        const types: Record<number, string> = {
            0: 'Pulse One',
            1: 'Pulse Pro',
            2: 'Pulse Hub',
        };
        return types[type] || 'Pulse';
    }

    /**
     * Calculate dewpoint from temperature and humidity
     */
    private calculateDewpoint(tempF: number, humidity: number): number {
        if (!tempF || !humidity) return 0;
        const tempC = (tempF - 32) * 5 / 9;
        const a = 17.27;
        const b = 237.7;
        const alpha = ((a * tempC) / (b + tempC)) + Math.log(humidity / 100);
        const dewpointC = (b * alpha) / (a - alpha);
        return (dewpointC * 9 / 5) + 32;
    }
}

// Singleton instance (initialized with API key from env)
let pulseServiceInstance: PulseService | null = null;

export function getPulseService(): PulseService | null {
    if (!pulseServiceInstance && process.env.PULSE_API_KEY) {
        pulseServiceInstance = new PulseService(process.env.PULSE_API_KEY);
    }
    return pulseServiceInstance;
}

export function initPulseService(apiKey: string): PulseService {
    pulseServiceInstance = new PulseService(apiKey);
    return pulseServiceInstance;
}
