import QtQuick
import Quickshell
import Quickshell.Io
import qs.Constants
import qs.Utils
pragma Singleton

// Location and weather service with decoupled geocoding and weather fetching.
Singleton {
    //console.log(JSON.stringify(weatherData))

    id: root

    property string locationFile: Paths.cacheDir + "location.json"
    property int weatherUpdateFrequency: 30 * 60 // 30 minutes expressed in seconds
    property bool isFetchingWeather: false
    readonly property alias data: adapter
    property string locationName: SettingsService.location
    property string latitude: SettingsService.latitude
    property string longitude: SettingsService.longitude
    property string stableLatitude: ""
    property string stableLongitude: ""
    property string stableName: ""
    // Formatted coordinates for UI display
    readonly property string displayCoordinates: {
        if (root.stableLatitude === "" || root.stableLongitude === "")
            return "";

        const lat = parseFloat(root.stableLatitude).toFixed(4);
        const lon = parseFloat(root.stableLongitude).toFixed(4);
        return `${lat}, ${lon}`;
    }
    // Weather condition detection
    readonly property int currentWeatherCode: data.weather ? data.weather.current_weather.weathercode : -1
    readonly property bool isDayTime: data.weather ? data.weather.current_weather.is_day : true
    readonly property bool isRaining: currentWeatherCode >= 0 && ((currentWeatherCode >= 51 && currentWeatherCode <= 67) || (currentWeatherCode >= 80 && currentWeatherCode <= 82) || (currentWeatherCode >= 95 && currentWeatherCode <= 99))
    readonly property bool isSnowing: currentWeatherCode >= 0 && ((currentWeatherCode >= 71 && currentWeatherCode <= 77) || (currentWeatherCode >= 85 && currentWeatherCode <= 86))
    readonly property bool isCloudy: currentWeatherCode >= 0 && (currentWeatherCode === 2 || currentWeatherCode === 3)
    readonly property bool isFoggy: currentWeatherCode >= 0 && (currentWeatherCode >= 40 && currentWeatherCode <= 49)
    readonly property bool isClearDay: currentWeatherCode >= 0 && (currentWeatherCode === 0 || currentWeatherCode === 1) && isDayTime
    readonly property bool isClearNight: currentWeatherCode >= 0 && (currentWeatherCode === 0 || currentWeatherCode === 1) && !isDayTime

    function resetWeather() {
        Logger.i("Location", "Resetting location and weather data");
        adapter.weatherLastFetch = 0;
        adapter.weather = null;
        update();
    }

    // Main update function - geocodes location if needed, then fetches weather if enabled
    function update() {
        updateWeatherData();
    }

    // Fetch weather data if enabled and coordinates are available
    function updateWeatherData() {
        if (isFetchingWeather) {
            Logger.w("Location", "Weather is still fetching");
            return ;
        }
        if (adapter.latitude === "" || adapter.longitude === "") {
            Logger.w("Location", "Cannot fetch weather without coordinates");
            return ;
        }
        const needsWeatherUpdate = (adapter.weather === null) || (Time.timestamp >= adapter.weatherLastFetch + weatherUpdateFrequency);
        if (needsWeatherUpdate) {
            isFetchingWeather = true;
            fetchWeatherData(root.latitude, root.longitude, errorCallback);
        }
    }

    // Fetch weather data from Open-Meteo API
    function fetchWeatherData(latitude, longitude, errorCallback) {
        Logger.d("Location", "Fetching weather from api.open-meteo.com");
        var url = "https://api.open-meteo.com/v1/forecast?latitude=" + latitude + "&longitude=" + longitude + "&current_weather=true&current=relativehumidity_2m,surface_pressure,is_day&daily=temperature_2m_max,temperature_2m_min,weathercode,sunset,sunrise&timezone=auto";
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function() {
            if (xhr.readyState === XMLHttpRequest.DONE) {
                if (xhr.status === 200) {
                    try {
                        var weatherData = JSON.parse(xhr.responseText);
                        // Save core data
                        data.weather = weatherData;
                        data.weatherLastFetch = Time.timestamp;
                        // Update stable display values only when complete and successful
                        SettingsService.latitude = data.latitude = weatherData.latitude.toString();
                        SettingsService.longitude = data.longitude = weatherData.longitude.toString();
                        root.coordinatesReady = true;
                        isFetchingWeather = false;
                        Logger.d("Location", "Cached weather to disk - stable coordinates updated");
                    } catch (e) {
                        errorCallback("Location", "Failed to parse weather data");
                    }
                } else {
                    errorCallback("Location", "Weather fetch error: " + xhr.status);
                }
            }
        };
        xhr.open("GET", url);
        xhr.send();
    }

    // --------------------------------
    function errorCallback(module, message) {
        Logger.e(module, message);
        isFetchingWeather = false;
    }

    // --------------------------------
    function weatherSymbolFromCode(code, isDay) {
        if (code === 0)
            return isDay ? "weather-sun" : "weather-moon";

        if (code === 1 || code === 2)
            return isDay ? "weather-cloud-sun" : "weather-moon-stars";

        if (code === 3)
            return "weather-cloud";

        if (code >= 45 && code <= 48)
            return "weather-cloud-haze";

        if (code >= 51 && code <= 67)
            return "weather-cloud-rain";

        if (code >= 80 && code <= 82)
            return "weather-cloud-rain";

        if (code >= 71 && code <= 77)
            return "weather-cloud-snow";

        if (code >= 71 && code <= 77)
            return "weather-cloud-snow";

        if (code >= 85 && code <= 86)
            return "weather-cloud-snow";

        if (code >= 95 && code <= 99)
            return "weather-cloud-lightning";

        return "weather-cloud";
    }

    // --------------------------------
    function weatherDescriptionFromCode(code) {
        if (code === 0)
            return "Clear sky";

        if (code === 1)
            return "Mainly clear";

        if (code === 2)
            return "Partly cloudy";

        if (code === 3)
            return "Overcast";

        if (code === 45 || code === 48)
            return "Fog";

        if (code >= 51 && code <= 67)
            return "Drizzle";

        if (code >= 71 && code <= 77)
            return "Snow";

        if (code >= 80 && code <= 82)
            return "Rain showers";

        if (code >= 95 && code <= 99)
            return "Thunderstorm";

        return "Unknown";
    }

    function weatherColorFromCode(code) {
        // Clear sky
        if (code === 0)
            return Colors.mYellow;

        // Mainly clear / Partly cloudy
        if (code === 1 || code === 2)
            return Colors.mSky;

        // Overcast
        if (code === 3)
            return Colors.mLavender;

        // Fog
        if (code === 45 || code === 48)
            return Colors.mCyan;

        // Drizzle / Rain / Rain showers
        if ((code >= 51 && code <= 55) || (code >= 61 && code <= 65) || (code >= 80 && code <= 82))
            return Colors.mBlue;

        // Freezing drizzle / Freezing rain
        if ((code >= 56 && code <= 57) || (code >= 66 && code <= 67))
            return Colors.mPurple;

        // Snow / Snow showers
        if ((code >= 71 && code <= 77) || (code >= 85 && code <= 86))
            return Colors.mLavender;

        // Thunderstorm
        if (code === 95)
            return Colors.mOrange;

        // Thunderstorm with hail
        if (code >= 96 && code <= 99)
            return Colors.mRed;

        return Colors.mSky;
    }

    FileView {
        id: locationFileView

        path: locationFile
        printErrors: false
        onAdapterUpdated: saveTimer.start()
        onLoaded: {
            Logger.d("Location", "Loaded cached data");
            update();
        }
        onLoadFailed: function(error) {
            update();
        }

        JsonAdapter {
            id: adapter

            property int weatherLastFetch: 0
            property var weather: null
        }

    }

    // Update timer runs when weather is enabled or location-based scheduling is active
    Timer {
        id: updateTimer

        interval: 20 * 1000
        running: true
        repeat: true
        onTriggered: {
            update();
        }
    }

    Timer {
        id: saveTimer

        running: false
        interval: 1000
        onTriggered: locationFileView.writeAdapter()
    }

}
