// Copyright Pacificao. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "HttpFwd.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "AgrarianTypes.h"
#include "AgrarianWeatherProviderSubsystem.generated.h"

class AAgrarianGameState;
class IHttpRequest;
class IHttpResponse;

USTRUCT(BlueprintType)
struct FAgrarianWeatherProviderSnapshot
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	FName TileId = NAME_None;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	float Latitude = 0.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	float Longitude = 0.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	FString Provider = TEXT("open-meteo");

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	FString ProviderTimestamp;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	float CurrentTemperatureC = 0.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	float DailyLowTemperatureC = 0.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	float DailyHighTemperatureC = 0.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	float PrecipitationMm = 0.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	float WindSpeedKmh = 0.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	float CloudCoverPercent = 0.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	float RelativeHumidityPercent = 0.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	float PressureMslHpa = 0.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	float VisibilityMeters = 10000.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	int32 WeatherCode = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	EAgrarianWeatherType MappedWeather = EAgrarianWeatherType::Clear;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	bool bIsValid = false;
};

USTRUCT(BlueprintType)
struct FAgrarianCachedWeatherSnapshot
{
	GENERATED_BODY()

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	FAgrarianWeatherProviderSnapshot Snapshot;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	FDateTime CachedAtUtc = FDateTime::MinValue();

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	float TimeToLiveSeconds = 900.0f;

	bool IsFresh(const FDateTime& NowUtc) const
	{
		return Snapshot.bIsValid
			&& CachedAtUtc != FDateTime::MinValue()
			&& (NowUtc - CachedAtUtc).GetTotalSeconds() <= TimeToLiveSeconds;
	}
};

UCLASS()
class UAgrarianWeatherProviderSubsystem : public UGameInstanceSubsystem
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	bool bEnableLiveWeatherRequests = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	FString OpenMeteoForecastEndpoint = TEXT("https://api.open-meteo.com/v1/forecast");

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	bool bEnableNoaaNwsFallbackForUSTiles = true;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	FString NoaaNwsPointsEndpoint = TEXT("https://api.weather.gov/points");

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	FString NoaaNwsUserAgent = TEXT("AgrarianGameMVP/0.1");

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather", meta = (ClampMin = "60.0"))
	float WeatherSnapshotCacheTtlSeconds = 900.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Agrarian|Weather")
	bool bEnableDeterministicFallbackWeather = true;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather")
	FAgrarianWeatherProviderSnapshot LastSnapshot;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather")
	FString LastNoaaNwsForecastGridDataUrl;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Agrarian|Weather")
	TMap<FString, FAgrarianCachedWeatherSnapshot> ServerWeatherSnapshotCache;

	UFUNCTION(BlueprintCallable, Category = "Agrarian|Weather")
	bool RequestWeatherForActiveGameState();

	UFUNCTION(BlueprintCallable, Category = "Agrarian|Weather")
	bool RequestWeatherForTile(FName TileId, float Latitude, float Longitude);

	UFUNCTION(BlueprintPure, Category = "Agrarian|Weather")
	FString BuildOpenMeteoForecastUrl(FName TileId, float Latitude, float Longitude) const;

	UFUNCTION(BlueprintCallable, Category = "Agrarian|Weather")
	bool RequestNoaaNwsFallbackForTile(FName TileId, float Latitude, float Longitude);

	UFUNCTION(BlueprintPure, Category = "Agrarian|Weather")
	bool IsNoaaNwsEligibleCoordinate(float Latitude, float Longitude) const;

	UFUNCTION(BlueprintPure, Category = "Agrarian|Weather")
	FString BuildNoaaNwsPointsUrl(float Latitude, float Longitude) const;

	UFUNCTION(BlueprintCallable, Category = "Agrarian|Weather")
	bool ApplySnapshotToGameState(const FAgrarianWeatherProviderSnapshot& Snapshot, AAgrarianGameState* GameState) const;

	UFUNCTION(BlueprintPure, Category = "Agrarian|Weather")
	FAgrarianMappedWeatherInputs MapSnapshotToAgrarianWeatherInputs(const FAgrarianWeatherProviderSnapshot& Snapshot) const;

	UFUNCTION(BlueprintCallable, Category = "Agrarian|Weather")
	bool TryApplyCachedSnapshot(FName TileId, const FString& Provider, AAgrarianGameState* GameState);

	UFUNCTION(BlueprintPure, Category = "Agrarian|Weather")
	bool HasFreshCachedSnapshot(FName TileId, const FString& Provider) const;

	UFUNCTION(BlueprintCallable, Category = "Agrarian|Weather")
	void ClearWeatherSnapshotCache();

	UFUNCTION(BlueprintCallable, Category = "Agrarian|Weather")
	bool ApplyDeterministicFallbackWeather(FName TileId, float Latitude, float Longitude, AAgrarianGameState* GameState);

	UFUNCTION(BlueprintPure, Category = "Agrarian|Weather")
	FAgrarianWeatherProviderSnapshot BuildDeterministicFallbackSnapshot(FName TileId, float Latitude, float Longitude, int32 DayOfYear, float HourOfDay) const;

	static EAgrarianWeatherType MapOpenMeteoWeatherCode(int32 WeatherCode, float PrecipitationMm, float WindSpeedKmh);

private:
	float GetDeterministicWeatherNoise(FName TileId, int32 DayOfYear, int32 Salt) const;
	FString MakeCacheKey(FName TileId, const FString& Provider) const;
	void CacheSnapshot(const FAgrarianWeatherProviderSnapshot& Snapshot, float TimeToLiveSeconds);
	void OnOpenMeteoResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, FName TileId, float Latitude, float Longitude);
	void OnNoaaNwsPointsResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, FName TileId, float Latitude, float Longitude);
	void OnNoaaNwsGridDataResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, FName TileId, float Latitude, float Longitude);
	bool ParseOpenMeteoForecast(const FString& ResponseContent, FName TileId, float Latitude, float Longitude, FAgrarianWeatherProviderSnapshot& OutSnapshot) const;
	bool ParseNoaaNwsGridData(const FString& ResponseContent, FName TileId, float Latitude, float Longitude, FAgrarianWeatherProviderSnapshot& OutSnapshot) const;
};
