package activityapi

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	activityv1 "hyapp.local/api/proto/activity/v1"
	"hyapp/pkg/appcode"
	"hyapp/services/gateway-service/internal/auth"
)

func TestGetAgencyOpeningStatusReturnsReviewWindowFields(t *testing.T) {
	client := &fakeAgencyOpeningClient{statusResp: agencyOpeningApprovedStatusResponse()}
	handler := New(Config{AgencyOpening: client})
	request := httptest.NewRequest(http.MethodGet, "/api/v1/activities/agency-opening-event", nil)
	request = request.WithContext(auth.WithUserID(appcode.WithContext(request.Context(), "lalu"), 42001))
	recorder := httptest.NewRecorder()

	handler.getAgencyOpeningStatus(recorder, request)

	if recorder.Code != http.StatusOK {
		t.Fatalf("status mismatch: got=%d body=%s", recorder.Code, recorder.Body.String())
	}
	if client.lastStatus == nil || client.lastStatus.GetUserId() != 42001 || client.lastStatus.GetMeta().GetAppCode() != "lalu" {
		t.Fatalf("status request mismatch: %+v", client.lastStatus)
	}

	var envelope struct {
		Code string `json:"code"`
		Data struct {
			Joined        bool `json:"joined"`
			Qualification struct {
				OpeningStatus string `json:"opening_status"`
			} `json:"qualification"`
			Application struct {
				Status            string `json:"status"`
				ApprovedAtMS      int64  `json:"approved_at_ms"`
				ScoreStartMS      int64  `json:"score_start_ms"`
				ScoreEndMS        int64  `json:"score_end_ms"`
				ReviewedByAdminID int64  `json:"reviewed_by_admin_id"`
			} `json:"application"`
			Rules []string `json:"rules"`
		} `json:"data"`
	}
	if err := json.Unmarshal(recorder.Body.Bytes(), &envelope); err != nil {
		t.Fatalf("decode response: %v", err)
	}
	if envelope.Code != "OK" || !envelope.Data.Joined || envelope.Data.Qualification.OpeningStatus != "Approved" {
		t.Fatalf("status envelope mismatch: %+v", envelope)
	}
	if envelope.Data.Application.Status != "approved" ||
		envelope.Data.Application.ApprovedAtMS != 2000 ||
		envelope.Data.Application.ScoreStartMS != 2000 ||
		envelope.Data.Application.ScoreEndMS != 2000+24*60*60*1000 ||
		envelope.Data.Application.ReviewedByAdminID != 7 {
		t.Fatalf("application fields mismatch: %+v", envelope.Data.Application)
	}
	if len(envelope.Data.Rules) < 2 || !strings.Contains(envelope.Data.Rules[1], "24 hours") {
		t.Fatalf("rules should explain 24-hour scoring window: %+v", envelope.Data.Rules)
	}
}

func TestApplyAgencyOpeningReturnsPendingReviewStatus(t *testing.T) {
	client := &fakeAgencyOpeningClient{
		applyResp:  agencyOpeningApplyResponse(),
		statusResp: agencyOpeningAppliedStatusResponse(),
	}
	handler := New(Config{AgencyOpening: client})
	request := httptest.NewRequest(http.MethodPost, "/api/v1/activities/agency-opening-event/apply", strings.NewReader(`{"command_id":"cmd-apply-1"}`))
	request = request.WithContext(auth.WithUserID(appcode.WithContext(request.Context(), "lalu"), 42001))
	recorder := httptest.NewRecorder()

	handler.applyAgencyOpening(recorder, request)

	if recorder.Code != http.StatusOK {
		t.Fatalf("status mismatch: got=%d body=%s", recorder.Code, recorder.Body.String())
	}
	if client.lastApply == nil || client.lastApply.GetUserId() != 42001 || client.lastApply.GetCommandId() != "cmd-apply-1" {
		t.Fatalf("apply request mismatch: %+v", client.lastApply)
	}

	var envelope struct {
		Code string `json:"code"`
		Data struct {
			Joined        bool `json:"joined"`
			Qualification struct {
				OpeningStatus string `json:"opening_status"`
			} `json:"qualification"`
			Application struct {
				Status       string `json:"status"`
				AppliedAtMS  int64  `json:"applied_at_ms"`
				ScoreStartMS int64  `json:"score_start_ms"`
			} `json:"application"`
		} `json:"data"`
	}
	if err := json.Unmarshal(recorder.Body.Bytes(), &envelope); err != nil {
		t.Fatalf("decode response: %v", err)
	}
	if envelope.Code != "OK" || !envelope.Data.Joined || envelope.Data.Qualification.OpeningStatus != "Pending Review" {
		t.Fatalf("apply response should show pending review: %+v", envelope)
	}
	if envelope.Data.Application.Status != "applied" || envelope.Data.Application.AppliedAtMS != 1000 || envelope.Data.Application.ScoreStartMS != 0 {
		t.Fatalf("apply application fields mismatch: %+v", envelope.Data.Application)
	}
}

type fakeAgencyOpeningClient struct {
	statusResp *activityv1.GetAgencyOpeningStatusResponse
	applyResp  *activityv1.ApplyAgencyOpeningResponse

	lastStatus *activityv1.GetAgencyOpeningStatusRequest
	lastApply  *activityv1.ApplyAgencyOpeningRequest
}

func (f *fakeAgencyOpeningClient) GetAgencyOpeningStatus(_ context.Context, req *activityv1.GetAgencyOpeningStatusRequest) (*activityv1.GetAgencyOpeningStatusResponse, error) {
	f.lastStatus = req
	if f.statusResp != nil {
		return f.statusResp, nil
	}
	return &activityv1.GetAgencyOpeningStatusResponse{}, nil
}

func (f *fakeAgencyOpeningClient) ApplyAgencyOpening(_ context.Context, req *activityv1.ApplyAgencyOpeningRequest) (*activityv1.ApplyAgencyOpeningResponse, error) {
	f.lastApply = req
	if f.applyResp != nil {
		return f.applyResp, nil
	}
	return &activityv1.ApplyAgencyOpeningResponse{}, nil
}

func (f *fakeAgencyOpeningClient) ListAgencyOpeningLeaderboard(context.Context, *activityv1.ListAgencyOpeningApplicationsRequest) (*activityv1.ListAgencyOpeningApplicationsResponse, error) {
	return &activityv1.ListAgencyOpeningApplicationsResponse{}, nil
}

func agencyOpeningApplyResponse() *activityv1.ApplyAgencyOpeningResponse {
	return &activityv1.ApplyAgencyOpeningResponse{
		Created: true,
		Application: &activityv1.AgencyOpeningApplication{
			ApplicationId:     "application-1",
			CycleId:           "cycle-1",
			AgencyId:          8001,
			AgencyOwnerUserId: 42001,
			AgencyName:        "Window Agency",
			HostCount:         12,
			Status:            "applied",
			AppliedAtMs:       1000,
		},
	}
}

func agencyOpeningAppliedStatusResponse() *activityv1.GetAgencyOpeningStatusResponse {
	resp := agencyOpeningBaseStatusResponse()
	resp.Joined = true
	resp.Eligible = false
	resp.Application = agencyOpeningApplyResponse().GetApplication()
	return resp
}

func agencyOpeningApprovedStatusResponse() *activityv1.GetAgencyOpeningStatusResponse {
	resp := agencyOpeningBaseStatusResponse()
	resp.Joined = true
	resp.Eligible = false
	resp.Application = &activityv1.AgencyOpeningApplication{
		ApplicationId:            "application-1",
		CycleId:                  "cycle-1",
		AgencyId:                 8001,
		AgencyOwnerUserId:        42001,
		AgencyName:               "Window Agency",
		HostCount:                12,
		Status:                   "approved",
		ScoreCoinAmount:          900,
		RewardThresholdCoinSpent: 500,
		RewardCoinAmount:         50,
		AppliedAtMs:              1000,
		ApprovedAtMs:             2000,
		ScoreStartMs:             2000,
		ScoreEndMs:               2000 + int64((24 * 60 * 60 * 1000)),
		ReviewedByAdminId:        7,
	}
	return resp
}

func agencyOpeningBaseStatusResponse() *activityv1.GetAgencyOpeningStatusResponse {
	return &activityv1.GetAgencyOpeningStatusResponse{
		ServerTimeMs: 3000,
		Enabled:      true,
		Eligible:     true,
		Joined:       false,
		Cycle: &activityv1.AgencyOpeningCycle{
			CycleId:      "cycle-1",
			Title:        "Agency Opening",
			Status:       "active",
			StartMs:      1,
			EndMs:        2000 + int64((48 * 60 * 60 * 1000)),
			MinHostCount: 10,
			Rewards: []*activityv1.AgencyOpeningReward{{
				RankNo:             1,
				ThresholdCoinSpent: 500,
				RewardCoinAmount:   50,
			}},
		},
		Agency: &activityv1.AgencyOpeningAgencySnapshot{
			AgencyId:          8001,
			OwnerUserId:       42001,
			Name:              "Window Agency",
			HostCount:         12,
			AgencyCreatedAtMs: 1,
			Status:            "active",
		},
	}
}
