package playback

import (
	"context"
	"io"
	"net/http"
	"strings"
	"testing"

	"mal/internal/db"
	"mal/internal/domain"
)

type wantSkipSegment struct {
	segmentType string
	start       float64
	end         float64
	source      string
}

func TestFetchSkipSegmentsFallsBackToOverridesWhenAniSkipFails(t *testing.T) {
	t.Parallel()

	svc := &playbackService{
		repo: &skipSegmentRepo{
			hasTable: true,
			overrides: []db.SkipSegmentOverrideRow{{
				UserID:    "user-1",
				AnimeID:   2167,
				Episode:   13,
				SkipType:  "op",
				StartTime: 90,
				EndTime:   180,
			}},
		},
		httpClient: &http.Client{Transport: roundTripFunc(func(*http.Request) (*http.Response, error) {
			return &http.Response{
				StatusCode: http.StatusInternalServerError,
				Body:       io.NopCloser(strings.NewReader("")),
				Header:     make(http.Header),
			}, nil
		})},
	}

	got, err := svc.fetchSkipSegments(context.Background(), "user-1", 2167, "13")
	if err != nil {
		t.Fatalf("fetchSkipSegments returned error with local fallback: %v", err)
	}
	if len(got) != 1 {
		t.Fatalf("len(got) = %d, want 1", len(got))
	}
	assertSkipSegment(t, got[0], wantSkipSegment{segmentType: "opening", start: 90, end: 180, source: "override"})
}

func TestFetchSkipSegmentsReturnsAniSkipErrorWhenNoOverrideFallbackExists(t *testing.T) {
	t.Parallel()

	svc := &playbackService{
		repo: &skipSegmentRepo{hasTable: true},
		httpClient: &http.Client{Transport: roundTripFunc(func(*http.Request) (*http.Response, error) {
			return &http.Response{
				StatusCode: http.StatusInternalServerError,
				Body:       io.NopCloser(strings.NewReader("")),
				Header:     make(http.Header),
			}, nil
		})},
	}

	got, err := svc.fetchSkipSegments(context.Background(), "user-1", 2167, "13")
	if err == nil {
		t.Fatal("fetchSkipSegments returned nil error without local fallback")
	}
	if got != nil {
		t.Fatalf("got segments = %+v, want nil", got)
	}
	if !strings.Contains(err.Error(), "aniskip: unexpected status: 500") {
		t.Fatalf("err = %v, want ani-skip status context", err)
	}
}

func TestFetchSkipSegmentsMergesOverridesWhenAniSkipSucceeds(t *testing.T) {
	t.Parallel()

	svc := &playbackService{
		repo: &skipSegmentRepo{
			hasTable: true,
			overrides: []db.SkipSegmentOverrideRow{{
				UserID:    "user-1",
				AnimeID:   2167,
				Episode:   13,
				SkipType:  "op",
				StartTime: 95,
				EndTime:   185,
			}},
		},
		httpClient: &http.Client{Transport: roundTripFunc(func(*http.Request) (*http.Response, error) {
			return &http.Response{
				StatusCode: http.StatusOK,
				Body: io.NopCloser(strings.NewReader(`{
					"found": true,
					"results": [
						{"skip_type": "op", "interval": {"start_time": 80, "end_time": 170}},
						{"skip_type": "ed", "interval": {"start_time": 1300, "end_time": 1390}}
					]
				}`)),
				Header: make(http.Header),
			}, nil
		})},
	}

	got, err := svc.fetchSkipSegments(context.Background(), "user-1", 2167, "13")
	if err != nil {
		t.Fatalf("fetchSkipSegments returned error: %v", err)
	}
	if len(got) != 2 {
		t.Fatalf("len(got) = %d, want 2", len(got))
	}
	assertSkipSegment(t, got[0], wantSkipSegment{segmentType: "opening", start: 95, end: 185, source: "override"})
	assertSkipSegment(t, got[1], wantSkipSegment{segmentType: "ending", start: 1300, end: 1390, source: "aniskip"})
}

func assertSkipSegment(t *testing.T, got domain.SkipSegment, want wantSkipSegment) {
	t.Helper()

	if got.Type != want.segmentType || got.Start != want.start || got.End != want.end || got.Source != want.source {
		t.Fatalf("got segment = %+v, want type=%q start=%v end=%v source=%q", got, want.segmentType, want.start, want.end, want.source)
	}
}

type roundTripFunc func(*http.Request) (*http.Response, error)

func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
	return f(req)
}

type skipSegmentRepo struct {
	domain.PlaybackRepository
	hasTable  bool
	hasErr    error
	overrides []db.SkipSegmentOverrideRow
	listErr   error
}

func (r *skipSegmentRepo) HasSkipSegmentOverrideTable(context.Context) (bool, error) {
	return r.hasTable, r.hasErr
}

func (r *skipSegmentRepo) ListSkipSegmentOverrides(context.Context, string, int64, int64) ([]db.SkipSegmentOverrideRow, error) {
	return r.overrides, r.listErr
}
