package main

import (
	"context"
	"errors"
	"sync"
	"testing"
)

func TestMain_RunSuccess(t *testing.T) {
	origRun := apiRunFn
	origFatal := apiFatalFn
	t.Cleanup(func() {
		apiRunFn = origRun
		apiFatalFn = origFatal
	})

	runCalled := false
	fatalCalled := false
	apiRunFn = func() error {
		runCalled = true
		return nil
	}
	apiFatalFn = func(v ...any) {
		fatalCalled = true
	}

	main()

	if !runCalled {
		t.Fatalf("expected main to call run function")
	}
	if fatalCalled {
		t.Fatalf("did not expect fatal function on successful run")
	}
}

func TestMain_RunErrorCallsFatal(t *testing.T) {
	origRun := apiRunFn
	origFatal := apiFatalFn
	t.Cleanup(func() {
		apiRunFn = origRun
		apiFatalFn = origFatal
	})

	errBoom := errors.New("boom")
	fatalCalled := false
	var fatalArgs []any

	apiRunFn = func() error {
		return errBoom
	}
	apiFatalFn = func(v ...any) {
		fatalCalled = true
		fatalArgs = v
	}

	main()

	if !fatalCalled {
		t.Fatalf("expected fatal function to be called on run error")
	}
	if len(fatalArgs) != 1 || fatalArgs[0] != errBoom {
		t.Fatalf("unexpected fatal args: %#v", fatalArgs)
	}
}

func TestServeAPI_StartError(t *testing.T) {
	errBoom := errors.New("boom")
	srv := &stubAPIServer{startErr: errBoom}

	err := serveAPI(context.Background(), srv, ":8080")
	if !errors.Is(err, errBoom) {
		t.Fatalf("expected %v, got %v", errBoom, err)
	}
	if srv.shutdownCalled {
		t.Fatalf("did not expect shutdown to be called on start error")
	}
}

func TestServeAPI_ShutsDownOnContextCancel(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	srv := &stubAPIServer{
		started:      make(chan struct{}),
		releaseStart: make(chan struct{}),
	}

	errCh := make(chan error, 1)
	go func() {
		errCh <- serveAPI(ctx, srv, ":8080")
	}()

	<-srv.started
	cancel()

	if err := <-errCh; err != nil {
		t.Fatalf("expected nil error, got %v", err)
	}
	if !srv.shutdownCalled {
		t.Fatalf("expected shutdown to be called")
	}
}

func TestServeAPI_ReturnsShutdownError(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	errBoom := errors.New("shutdown failed")
	srv := &stubAPIServer{
		started:      make(chan struct{}),
		releaseStart: make(chan struct{}),
		shutdownErr:  errBoom,
	}

	errCh := make(chan error, 1)
	go func() {
		errCh <- serveAPI(ctx, srv, ":8080")
	}()

	<-srv.started
	cancel()

	if err := <-errCh; !errors.Is(err, errBoom) {
		t.Fatalf("expected %v, got %v", errBoom, err)
	}
}

type stubAPIServer struct {
	startErr       error
	shutdownErr    error
	started        chan struct{}
	releaseStart   chan struct{}
	shutdownCalled bool
	releaseOnce    sync.Once
}

func (s *stubAPIServer) Start(string) error {
	if s.started != nil {
		close(s.started)
	}
	if s.releaseStart != nil {
		<-s.releaseStart
	}
	return s.startErr
}

func (s *stubAPIServer) Shutdown(context.Context) error {
	s.shutdownCalled = true
	if s.releaseStart != nil {
		s.releaseOnce.Do(func() {
			close(s.releaseStart)
		})
	}
	return s.shutdownErr
}
