// service_test.go validates application service delegation behavior.
// Layer: internal/app tests for read use-case orchestration.
package app

import (
	"context"
	"errors"
	"testing"

	"gitea.maximumdirect.net/ejr/weatherfeeder/model"
)

type fakeRepository struct {
	observation *model.WeatherObservation
	forecast    *model.WeatherForecastRun
	alerts      *model.WeatherAlertRun
	conditions  *CurrentConditions
	err         error

	currentConditionsWindow int
}

func (r *fakeRepository) LatestObservation(context.Context) (*model.WeatherObservation, error) {
	return r.observation, r.err
}

func (r *fakeRepository) LatestHourlyForecast(context.Context) (*model.WeatherForecastRun, error) {
	return r.forecast, r.err
}

func (r *fakeRepository) LatestAlertRun(context.Context) (*model.WeatherAlertRun, error) {
	return r.alerts, r.err
}

func (r *fakeRepository) CurrentConditions(_ context.Context, observationWindowMinutes int) (*CurrentConditions, error) {
	r.currentConditionsWindow = observationWindowMinutes
	return r.conditions, r.err
}

func TestServiceDelegatesObservation(t *testing.T) {
	repo := &fakeRepository{observation: &model.WeatherObservation{StationID: "KSTL"}}
	svc := NewService(repo)

	obs, err := svc.LatestObservation(context.Background())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if obs == nil || obs.StationID != "KSTL" {
		t.Fatalf("unexpected observation: %+v", obs)
	}
}

func TestServiceDelegatesForecast(t *testing.T) {
	repo := &fakeRepository{forecast: &model.WeatherForecastRun{LocationID: "stl"}}
	svc := NewService(repo)

	run, err := svc.LatestHourlyForecast(context.Background())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if run == nil || run.LocationID != "stl" {
		t.Fatalf("unexpected forecast: %+v", run)
	}
}

func TestServiceDelegatesAlerts(t *testing.T) {
	repo := &fakeRepository{alerts: &model.WeatherAlertRun{LocationID: "stl"}}
	svc := NewService(repo)

	run, err := svc.LatestAlertRun(context.Background())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if run == nil || run.LocationID != "stl" {
		t.Fatalf("unexpected alert run: %+v", run)
	}
}

func TestServiceUsesDefaultCurrentConditionsWindow(t *testing.T) {
	repo := &fakeRepository{conditions: &CurrentConditions{ConditionCode: model.WMOUnknown}}
	svc := NewService(repo)

	_, err := svc.CurrentConditions(context.Background())
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if repo.currentConditionsWindow != ObservationWindowMinutesDefault {
		t.Fatalf("expected observation window %d, got %d", ObservationWindowMinutesDefault, repo.currentConditionsWindow)
	}
}

func TestServicePropagatesErrors(t *testing.T) {
	want := errors.New("boom")
	repo := &fakeRepository{err: want}
	svc := NewService(repo)

	if _, err := svc.LatestObservation(context.Background()); !errors.Is(err, want) {
		t.Fatalf("expected error %v, got %v", want, err)
	}
}
