package handler

import (
	"baron-sso-backend/internal/domain"
	"baron-sso-backend/internal/service"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/stretchr/testify/assert"
)

// --- Helper ---

func newLinkedRpTestApp(h *AuthHandler) *fiber.App {
	app := fiber.New()
	app.Get("/api/v1/user/rp/linked", h.ListLinkedRps)
	return app
}

// --- Tests ---

func TestListLinkedRps_PriorityAndAggregation(t *testing.T) {
	transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
		switch r.URL.Host {
		case "kratos.test":
			if r.URL.Path == "/sessions/whoami" {
				if r.Header.Get("X-Session-Token") == "" && r.Header.Get("Cookie") == "" {
					return httpResponse(r, http.StatusUnauthorized, "unauthorized"), nil
				}
				return httpJSONAny(r, http.StatusOK, map[string]any{
					"identity": map[string]any{
						"id": "user-123",
						"traits": map[string]any{
							"email": "user@test.com",
						},
					},
				}), nil
			}
		case "hydra.test":
			if r.URL.Path == "/oauth2/auth/sessions/consent" {
				return httpJSONAny(r, http.StatusOK, []map[string]any{
					{
						"client": map[string]any{
							"client_id":   "devfront",
							"client_name": "DevFront",
							"redirect_uris": []string{
								"https://active.example.com/callback",
							},
						},
						"grant_scope": []string{"openid", "profile"},
						"handled_at":  time.Now().Format(time.RFC3339),
					},
					{
						"client": map[string]any{
							"client_id":   "orgfront",
							"client_name": "OrgFront",
							"metadata": map[string]any{
								"auto_login_supported": true,
								"auto_login_url":       "http://localhost:5175/login",
							},
							"redirect_uris": []string{
								"http://localhost:5175/auth/callback",
							},
						},
						"grant_scope": []string{"openid", "profile"},
						"handled_at":  time.Now().Format(time.RFC3339),
					},
				}), nil
			}
			if r.URL.Path == "/admin/clients/client-audit" {
				return httpJSONAny(r, http.StatusOK, map[string]any{
					"client_id":   "client-audit",
					"client_name": "Audit App",
				}), nil
			}
			if r.URL.Path == "/admin/clients/client-consent" {
				return httpJSONAny(r, http.StatusOK, map[string]any{
					"client_id":   "client-consent",
					"client_name": "Consent App",
				}), nil
			}
		}
		return httpResponse(r, http.StatusNotFound, "not found"), nil
	})

	client := &http.Client{Transport: transport}

	origDefault := http.DefaultClient
	http.DefaultClient = client
	defer func() {
		http.DefaultClient = origDefault
	}()

	auditRepo := &mockAuditRepo{
		logs: []domain.AuditLog{
			{
				UserID:    "user-123",
				EventType: "consent.granted",
				Timestamp: time.Now().Add(-10 * time.Hour),
				Details:   `{"client_id":"client-audit", "scopes":["audit_scope"]}`,
			},
		},
	}

	consentRepo := &mockConsentRepo{
		consents: []domain.ClientConsent{
			{
				Subject:       "user-123",
				ClientID:      "client-consent",
				GrantedScopes: []string{"consent_scope"},
				UpdatedAt:     time.Now().Add(-2 * time.Hour),
			},
		},
	}

	h := &AuthHandler{
		Hydra: &service.HydraAdminService{
			AdminURL:   "http://hydra.test",
			HTTPClient: client,
		},
		AuditRepo:   auditRepo,
		ConsentRepo: consentRepo,
		KratosAdmin: new(MockKratosAdminService),
	}

	t.Setenv("KRATOS_PUBLIC_URL", "http://kratos.test")
	t.Setenv("KRATOS_ADMIN_URL", "http://kratos.test")
	t.Setenv("HYDRA_PUBLIC_URL", "https://sso.example.com/oidc")
	t.Setenv("DEVFRONT_URL", "http://localhost:5174")

	app := newLinkedRpTestApp(h)

	req := httptest.NewRequest(http.MethodGet, "/api/v1/user/rp/linked", nil)
	req.Header.Set("Cookie", "ory_kratos_session=valid")

	resp, err := app.Test(req)
	assert.NoError(t, err)
	assert.Equal(t, http.StatusOK, resp.StatusCode)

	var res struct {
		Items []struct {
			ID                 string   `json:"id"`
			Name               string   `json:"name"`
			Status             string   `json:"status"`
			Scopes             []string `json:"scopes"`
			InitURL            string   `json:"init_url"`
			AutoLoginSupported bool     `json:"auto_login_supported"`
			AutoLoginURL       string   `json:"auto_login_url"`
		} `json:"items"`
	}
	json.NewDecoder(resp.Body).Decode(&res)

	assert.Equal(t, 4, len(res.Items))

	statusMap := make(map[string]string)
	for _, item := range res.Items {
		statusMap[item.ID] = item.Status
	}

	assert.Equal(t, "active", statusMap["devfront"])
	assert.Equal(t, "active", statusMap["orgfront"])
	assert.Equal(t, "inactive", statusMap["client-consent"])
	assert.Equal(t, "inactive", statusMap["client-audit"])

	var activeInitURL string
	for _, item := range res.Items {
		if item.ID == "devfront" {
			activeInitURL = item.InitURL
			break
		}
	}

	parsedInitURL, err := url.Parse(activeInitURL)
	assert.NoError(t, err)
	assert.Equal(t, "http", parsedInitURL.Scheme)
	assert.Equal(t, "localhost:5174", parsedInitURL.Host)
	assert.Equal(t, "/login", parsedInitURL.Path)
	assert.Equal(t, "1", parsedInitURL.Query().Get("auto"))
	assert.Equal(t, "/clients", parsedInitURL.Query().Get("returnTo"))

	var orgfrontItem struct {
		InitURL            string
		AutoLoginSupported bool
		AutoLoginURL       string
	}
	for _, item := range res.Items {
		if item.ID == "orgfront" {
			orgfrontItem.InitURL = item.InitURL
			orgfrontItem.AutoLoginSupported = item.AutoLoginSupported
			orgfrontItem.AutoLoginURL = item.AutoLoginURL
			break
		}
	}
	assert.True(t, orgfrontItem.AutoLoginSupported)
	assert.Equal(t, "http://localhost:5175/login?auto=1", orgfrontItem.AutoLoginURL)
	assert.Equal(t, orgfrontItem.AutoLoginURL, orgfrontItem.InitURL)
}

func TestListLinkedRps_EnrichesLogoFromHydraClientWhenConsentSessionOmitsMetadata(t *testing.T) {
	transport := roundTripFunc(func(r *http.Request) (*http.Response, error) {
		switch r.URL.Host {
		case "kratos.test":
			if r.URL.Path == "/sessions/whoami" {
				return httpJSONAny(r, http.StatusOK, map[string]any{
					"identity": map[string]any{
						"id": "user-123",
					},
				}), nil
			}
		case "hydra.test":
			if r.URL.Path == "/oauth2/auth/sessions/consent" {
				return httpJSONAny(r, http.StatusOK, []map[string]any{
					{
						"client": map[string]any{
							"client_id":   "gitea-client",
							"client_name": "Gitea",
							"redirect_uris": []string{
								"https://gitea.example.com/callback",
							},
						},
						"grant_scope": []string{"openid", "profile"},
						"handled_at":  time.Now().Format(time.RFC3339),
					},
				}), nil
			}
			if r.URL.Path == "/clients/gitea-client" {
				return httpJSONAny(r, http.StatusOK, map[string]any{
					"client_id":   "gitea-client",
					"client_name": "Gitea",
					"redirect_uris": []string{
						"https://gitea.example.com/callback",
					},
					"metadata": map[string]any{
						"logo_url": "https://cdn.example.com/gitea.svg",
					},
				}), nil
			}
		}
		return httpResponse(r, http.StatusNotFound, "not found"), nil
	})

	client := &http.Client{Transport: transport}

	origDefault := http.DefaultClient
	http.DefaultClient = client
	defer func() {
		http.DefaultClient = origDefault
	}()

	h := &AuthHandler{
		Hydra: &service.HydraAdminService{
			AdminURL:   "http://hydra.test",
			HTTPClient: client,
		},
		KratosAdmin: new(MockKratosAdminService),
	}

	t.Setenv("KRATOS_PUBLIC_URL", "http://kratos.test")
	t.Setenv("KRATOS_ADMIN_URL", "http://kratos.test")
	t.Setenv("HYDRA_PUBLIC_URL", "https://sso.example.com/oidc")

	app := newLinkedRpTestApp(h)

	req := httptest.NewRequest(http.MethodGet, "/api/v1/user/rp/linked", nil)
	req.Header.Set("Cookie", "ory_kratos_session=valid")

	resp, err := app.Test(req)
	assert.NoError(t, err)
	assert.Equal(t, http.StatusOK, resp.StatusCode)

	var res struct {
		Items []struct {
			ID   string `json:"id"`
			Logo string `json:"logo"`
		} `json:"items"`
	}
	json.NewDecoder(resp.Body).Decode(&res)

	assert.Len(t, res.Items, 1)
	assert.Equal(t, "gitea-client", res.Items[0].ID)
	assert.Equal(t, "https://cdn.example.com/gitea.svg", res.Items[0].Logo)
}
