package main

import (
	"io"
	"log"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/seifghazi/claude-code-monitor/internal/config"
	httphandler "github.com/seifghazi/claude-code-monitor/internal/handler"
	"github.com/seifghazi/claude-code-monitor/internal/model"
	"github.com/seifghazi/claude-code-monitor/internal/service"
)

type routerStorageStub struct {
	getUsageStatsFn func(startDate, endDate, modelFilter, orgFilter string) (*model.UsageStats, error)
}

func (s *routerStorageStub) SaveRequest(*model.RequestLog) (string, error) { panic("unexpected call") }
func (s *routerStorageStub) GetRequests(int, int, string) ([]model.RequestLog, int, error) {
	panic("unexpected call")
}
func (s *routerStorageStub) GetAllRequests(string) ([]*model.RequestLog, error) {
	panic("unexpected call")
}
func (s *routerStorageStub) GetRequestByShortID(string) (*model.RequestLog, string, error) {
	panic("unexpected call")
}
func (s *routerStorageStub) ClearRequests() (int, error) { panic("unexpected call") }
func (s *routerStorageStub) UpdateRequestWithGrading(string, *model.PromptGrade) error {
	panic("unexpected call")
}
func (s *routerStorageStub) UpdateRequestWithResponse(*model.RequestLog) error {
	panic("unexpected call")
}
func (s *routerStorageStub) DeleteRequestsOlderThan(time.Duration) (int, error) {
	panic("unexpected call")
}
func (s *routerStorageStub) GetDatabaseStats() (map[string]interface{}, error) {
	panic("unexpected call")
}
func (s *routerStorageStub) GetUsageStats(startDate, endDate, modelFilter, orgFilter string) (*model.UsageStats, error) {
	if s.getUsageStatsFn != nil {
		return s.getUsageStatsFn(startDate, endDate, modelFilter, orgFilter)
	}
	return &model.UsageStats{}, nil
}
func (s *routerStorageStub) GetRequestsSummary(string) ([]*model.RequestSummary, error) {
	panic("unexpected call")
}
func (s *routerStorageStub) GetRequestsSummaryPaginated(string, string, string, int, int) ([]*model.RequestSummary, int, error) {
	panic("unexpected call")
}
func (s *routerStorageStub) GetStats(string, string, string) (*model.DashboardStats, error) {
	panic("unexpected call")
}
func (s *routerStorageStub) GetHourlyStats(string, string, int, string) (*model.HourlyStatsResponse, error) {
	panic("unexpected call")
}
func (s *routerStorageStub) GetModelStats(string, string, string) (*model.ModelStatsResponse, error) {
	panic("unexpected call")
}
func (s *routerStorageStub) GetLatestRequestDate() (*time.Time, error) { panic("unexpected call") }
func (s *routerStorageStub) GetDistinctOrganizations() ([]string, error) {
	panic("unexpected call")
}
func (s *routerStorageStub) GetSettings() (*model.ProxySettings, error) {
	panic("unexpected call")
}
func (s *routerStorageStub) SaveSettings(*model.ProxySettings) error { panic("unexpected call") }
func (s *routerStorageStub) GetConfig() *config.StorageConfig        { return &config.StorageConfig{} }
func (s *routerStorageStub) EnsureDirectoryExists() error            { return nil }
func (s *routerStorageStub) Close() error                            { return nil }

var _ service.StorageService = (*routerStorageStub)(nil)

func newRouterUnderTest(t *testing.T, cfg *config.Config, storage service.StorageService) http.Handler {
	t.Helper()

	h := httphandler.New(storage, log.New(io.Discard, "", 0), nil, nil, false)
	return buildHandler(cfg, h)
}

func TestBuildHandlerKeepsDiscoveryRoutesPublicWhileProtectingDashboard(t *testing.T) {
	cfg := &config.Config{
		Auth: config.AuthConfig{
			Enabled:           true,
			Token:             "proxy-secret",
			APIKeyHeader:      "X-API-Key",
			DashboardPassword: "dashboard-secret",
		},
		CORS: config.CORSConfig{
			AllowedOrigins: []string{"*"},
			AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
			AllowedHeaders: []string{"Authorization", "Content-Type", "X-API-Key"},
		},
	}

	router := newRouterUnderTest(t, cfg, &routerStorageStub{
		getUsageStatsFn: func(startDate, endDate, modelFilter, orgFilter string) (*model.UsageStats, error) {
			return &model.UsageStats{TotalRequests: 11}, nil
		},
	})

	for _, path := range []string{"/health", "/openapi.json", "/openapi.yaml"} {
		req := httptest.NewRequest(http.MethodGet, path, nil)
		req.RemoteAddr = "10.0.0.5:12345"
		rr := httptest.NewRecorder()

		router.ServeHTTP(rr, req)

		if rr.Code != http.StatusOK {
			t.Fatalf("expected %s to stay public, got %d", path, rr.Code)
		}
	}

	req := httptest.NewRequest(http.MethodGet, "/api/stats", nil)
	req.RemoteAddr = "10.0.0.5:12345"
	rr := httptest.NewRecorder()
	router.ServeHTTP(rr, req)

	if rr.Code != http.StatusUnauthorized {
		t.Fatalf("expected dashboard endpoint to require auth, got %d", rr.Code)
	}
}

func TestBuildHandlerRequiresBothProxyAndDashboardCredentialsForDashboardAPI(t *testing.T) {
	cfg := &config.Config{
		Auth: config.AuthConfig{
			Enabled:           true,
			Token:             "proxy-secret",
			APIKeyHeader:      "X-API-Key",
			DashboardPassword: "dashboard-secret",
		},
		CORS: config.CORSConfig{
			AllowedOrigins: []string{"*"},
			AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
			AllowedHeaders: []string{"Authorization", "Content-Type", "X-API-Key"},
		},
	}

	router := newRouterUnderTest(t, cfg, &routerStorageStub{
		getUsageStatsFn: func(startDate, endDate, modelFilter, orgFilter string) (*model.UsageStats, error) {
			return &model.UsageStats{TotalRequests: 5}, nil
		},
	})

	testCases := []struct {
		name       string
		setup      func(*http.Request)
		wantStatus int
	}{
		{
			name: "proxy auth only",
			setup: func(r *http.Request) {
				r.Header.Set("X-API-Key", "proxy-secret")
			},
			wantStatus: http.StatusUnauthorized,
		},
		{
			name: "dashboard auth only",
			setup: func(r *http.Request) {
				r.SetBasicAuth("admin", "dashboard-secret")
			},
			wantStatus: http.StatusUnauthorized,
		},
		{
			name: "both auth layers",
			setup: func(r *http.Request) {
				r.Header.Set("X-API-Key", "proxy-secret")
				r.SetBasicAuth("admin", "dashboard-secret")
			},
			wantStatus: http.StatusOK,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			req := httptest.NewRequest(http.MethodGet, "/api/stats", nil)
			req.RemoteAddr = "10.0.0.5:12345"
			tc.setup(req)
			rr := httptest.NewRecorder()

			router.ServeHTTP(rr, req)

			if rr.Code != tc.wantStatus {
				t.Fatalf("expected status %d, got %d", tc.wantStatus, rr.Code)
			}
		})
	}
}
