package handlers

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"math"
	"net/http"
	"strconv"
	"strings"
	"time"

	"mgit.msbls.de/m/paliad/internal/auth"
	"mgit.msbls.de/m/paliad/internal/calc"
)

// Standard Streitwerte for precomputed table rows.
var standardStreitwerte = []float64{
	500, 1000, 2000, 3000, 5000,
	10000, 15000, 20000, 25000,
	30000, 40000, 50000,
	75000, 100000, 150000, 200000,
	250000, 300000, 400000, 500000,
	750000, 1000000, 1500000, 2000000,
	3000000, 4000000, 5000000,
	7500000, 10000000, 15000000, 20000000,
	25000000, 30000000, 50000000,
}

// feeVersionOrder defines the display order for GKG/RVG versions (newest first).
var feeVersionOrder = []string{"2025", "2021", "2013", "2005"}

// --- Response types ---

type FeeTableResponse struct {
	GKG         []FeeVersionTable `json:"gkg"`
	RVG         []FeeVersionTable `json:"rvg"`
	UPC         []UPCScheduleInfo `json:"upc"`
	EPA         []EPAFeeInfo      `json:"epa"`
	Multipliers []MultiplierInfo  `json:"multipliers"`
	PatKostG    PatKostGInfo      `json:"patkostg"`
}

type FeeVersionTable struct {
	Version   string        `json:"version"`
	Label     string        `json:"label"`
	ValidFrom string        `json:"validFrom"`
	IsCurrent bool          `json:"isCurrent"`
	Rows      []FeeTableRow `json:"rows"`
}

type FeeTableRow struct {
	Streitwert float64 `json:"streitwert"`
	Fee        float64 `json:"fee"`
}

type UPCScheduleInfo struct {
	Version           string          `json:"version"`
	Label             string          `json:"label"`
	FixedInfringement float64         `json:"fixedInfringement"`
	FixedRevocation   float64         `json:"fixedRevocation"`
	SMEReduction      float64         `json:"smeReduction"`
	ValueBased        []UPCValueRow   `json:"valueBased"`
	RecoverableCosts  []UPCRecoverRow `json:"recoverableCosts"`
}

type UPCValueRow struct {
	MaxValue *float64 `json:"maxValue"`
	Fee      float64  `json:"fee"`
}

type UPCRecoverRow struct {
	MaxValue *float64 `json:"maxValue"`
	Ceiling  float64  `json:"ceiling"`
}

type EPAFeeInfo struct {
	Key     string  `json:"key"`
	Label   string  `json:"label"`
	LabelEN string  `json:"labelEN"`
	Fee     float64 `json:"fee"`
	SMEFee  float64 `json:"smeFee"`
}

type MultiplierInfo struct {
	Key            string  `json:"key"`
	Label          string  `json:"label"`
	LabelEN        string  `json:"labelEN"`
	CourtFeeFactor float64 `json:"courtFeeFactor"`
	FeeBasis       string  `json:"feeBasis"`
	RAVGFactor     float64 `json:"raVGFactor"`
	RATGFactor     float64 `json:"raTGFactor"`
	PAVGFactor     float64 `json:"paVGFactor"`
	PATGFactor     float64 `json:"paTGFactor"`
}

type PatKostGInfo struct {
	CourtFees  []PatKostGCourtFee `json:"courtFees"`
	DPMAFees   []DPMAFee          `json:"dpmaFees"`
	AnnualFees []DPMAAnnualFee    `json:"annualFees"`
}

type PatKostGCourtFee struct {
	Key     string  `json:"key"`
	Label   string  `json:"label"`
	LabelEN string  `json:"labelEN"`
	Factor  float64 `json:"factor"`
	Note    string  `json:"note"`
	NoteEN  string  `json:"noteEN"`
}

type DPMAFee struct {
	Label   string  `json:"label"`
	LabelEN string  `json:"labelEN"`
	Fee     float64 `json:"fee"`
}

type DPMAAnnualFee struct {
	Year int     `json:"year"`
	Fee  float64 `json:"fee"`
}

// --- Lookup response ---

type LookupResponse struct {
	Streitwert float64                    `json:"streitwert"`
	GKG        map[string]float64         `json:"gkg"`
	RVG        map[string]float64         `json:"rvg"`
	UPC        map[string]UPCLookupResult `json:"upc"`
}

type UPCLookupResult struct {
	FixedInfringement  float64 `json:"fixedInfringement"`
	ValueBasedFee      float64 `json:"valueBasedFee"`
	Total              float64 `json:"total"`
	SMETotal           float64 `json:"smeTotal"`
	RecoverableCeiling float64 `json:"recoverableCeiling"`
}

// --- Feedback ---

type GebuehrenFeedback struct {
	FeedbackType string `json:"feedback_type"`
	Message      string `json:"message"`
	Schedule     string `json:"schedule"`
}

func handleGebuehrentabellenPage(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "dist/gebuehrentabellen.html")
}

func handleGebuehrentabellenAPI(w http.ResponseWriter, r *http.Request) {
	resp := buildFeeTableResponse()
	writeJSON(w, http.StatusOK, resp)
}

func handleGebuehrentabellenLookup(w http.ResponseWriter, r *http.Request) {
	sw := r.URL.Query().Get("streitwert")
	if sw == "" {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "streitwert parameter required"})
		return
	}
	streitwert, err := strconv.ParseFloat(sw, 64)
	if err != nil || streitwert <= 0 {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid streitwert"})
		return
	}

	result := LookupResponse{
		Streitwert: streitwert,
		GKG:        make(map[string]float64),
		RVG:        make(map[string]float64),
		UPC:        make(map[string]UPCLookupResult),
	}

	for _, v := range feeVersionOrder {
		gkgFee, _ := calc.ComputeBaseFee(streitwert, false, v)
		rvgFee, _ := calc.ComputeBaseFee(streitwert, true, v)
		result.GKG[v] = math.Round(gkgFee*100) / 100
		result.RVG[v] = math.Round(rvgFee*100) / 100
	}

	for key, schedule := range calc.UPCFeeSchedules {
		valueFee := upcValueFee(streitwert, schedule.ValueBased)
		total := schedule.FixedInfringement + valueFee
		smeTotal := math.Round(total * (1 - schedule.SMEReduction))
		recov := upcRecoverableCeiling(streitwert, schedule.RecoverableCosts)
		result.UPC[key] = UPCLookupResult{
			FixedInfringement:  schedule.FixedInfringement,
			ValueBasedFee:      valueFee,
			Total:              total,
			SMETotal:           smeTotal,
			RecoverableCeiling: recov,
		}
	}

	writeJSON(w, http.StatusOK, result)
}

func upcValueFee(streitwert float64, brackets []calc.UPCFeeBracket) float64 {
	for _, b := range brackets {
		if b.MaxValue == nil || streitwert <= *b.MaxValue {
			return b.Fee
		}
	}
	return brackets[len(brackets)-1].Fee
}

func upcRecoverableCeiling(streitwert float64, table []calc.UPCRecoverableCost) float64 {
	for _, e := range table {
		if e.MaxValue == nil || streitwert <= *e.MaxValue {
			return e.Ceiling
		}
	}
	return table[len(table)-1].Ceiling
}

func buildFeeTableResponse() FeeTableResponse {
	var resp FeeTableResponse

	// GKG tables
	for _, v := range feeVersionOrder {
		schedule := calc.FeeSchedules[v]
		table := FeeVersionTable{
			Version:   v,
			Label:     schedule.Label,
			ValidFrom: schedule.ValidFrom,
			IsCurrent: v == "2025",
		}
		for _, sw := range standardStreitwerte {
			fee, _ := calc.ComputeBaseFee(sw, false, v)
			table.Rows = append(table.Rows, FeeTableRow{
				Streitwert: sw,
				Fee:        math.Round(fee*100) / 100,
			})
		}
		resp.GKG = append(resp.GKG, table)
	}

	// RVG tables
	for _, v := range feeVersionOrder {
		schedule := calc.FeeSchedules[v]
		table := FeeVersionTable{
			Version:   v,
			Label:     schedule.Label,
			ValidFrom: schedule.ValidFrom,
			IsCurrent: v == "2025",
		}
		for _, sw := range standardStreitwerte {
			fee, _ := calc.ComputeBaseFee(sw, true, v)
			table.Rows = append(table.Rows, FeeTableRow{
				Streitwert: sw,
				Fee:        math.Round(fee*100) / 100,
			})
		}
		resp.RVG = append(resp.RVG, table)
	}

	// UPC schedules (2026 first, then pre2026)
	for _, key := range []string{"2026", "pre2026"} {
		schedule := calc.UPCFeeSchedules[key]
		info := UPCScheduleInfo{
			Version:           key,
			Label:             schedule.Label,
			FixedInfringement: schedule.FixedInfringement,
			FixedRevocation:   schedule.FixedRevocation,
			SMEReduction:      schedule.SMEReduction,
		}
		for _, b := range schedule.ValueBased {
			info.ValueBased = append(info.ValueBased, UPCValueRow{
				MaxValue: b.MaxValue,
				Fee:      b.Fee,
			})
		}
		for _, r := range schedule.RecoverableCosts {
			info.RecoverableCosts = append(info.RecoverableCosts, UPCRecoverRow{
				MaxValue: r.MaxValue,
				Ceiling:  r.Ceiling,
			})
		}
		resp.UPC = append(resp.UPC, info)
	}

	// EPA fees
	for _, key := range []string{"EPA_OPPOSITION", "EPA_APPEAL"} {
		epa := calc.EPAFees[key]
		resp.EPA = append(resp.EPA, EPAFeeInfo{
			Key:     epa.Key,
			Label:   epa.Label,
			LabelEN: epa.LabelEN,
			Fee:     epa.Fee,
			SMEFee:  epa.SMEFee,
		})
	}
	// Additional EPA fees not in calc (grant-related)
	resp.EPA = append(resp.EPA,
		EPAFeeInfo{Key: "EPA_GRANT", Label: "Erteilungs- und Druckgebühr", LabelEN: "Grant and printing fee", Fee: 1040, SMEFee: 1040},
		EPAFeeInfo{Key: "EPA_EXAMINATION", Label: "Prüfungsgebühr", LabelEN: "Examination fee", Fee: 1970, SMEFee: 1970},
		EPAFeeInfo{Key: "EPA_SEARCH", Label: "Recherchengebühr", LabelEN: "Search fee", Fee: 1520, SMEFee: 1520},
		EPAFeeInfo{Key: "EPA_FILING", Label: "Anmeldegebühr", LabelEN: "Filing fee", Fee: 140, SMEFee: 140},
		EPAFeeInfo{Key: "EPA_DESIGNATION", Label: "Benennungsgebühr (pauschal)", LabelEN: "Designation fee (flat)", Fee: 660, SMEFee: 660},
	)

	// Multipliers
	for _, inst := range calc.AllDEInstances() {
		resp.Multipliers = append(resp.Multipliers, MultiplierInfo{
			Key:            inst.Key,
			Label:          inst.Label,
			LabelEN:        inst.LabelEN,
			CourtFeeFactor: inst.CourtFeeFactor,
			FeeBasis:       inst.FeeBasis,
			RAVGFactor:     inst.RAVGFactor,
			RATGFactor:     inst.RATGFactor,
			PAVGFactor:     inst.PAVGFactor,
			PATGFactor:     inst.PATGFactor,
		})
	}

	// PatKostG
	resp.PatKostG = PatKostGInfo{
		CourtFees: []PatKostGCourtFee{
			{
				Key: "BPatG", Label: "BPatG (Nichtigkeitsverfahren)", LabelEN: "Federal Patent Court (Nullity)",
				Factor: 4.5, Note: "4,5-fache GKG-Gebühr", NoteEN: "4.5x GKG fee",
			},
			{
				Key: "BGH_NULLITY", Label: "BGH (Nichtigkeitsberufung)", LabelEN: "Federal Court of Justice (Nullity Appeal)",
				Factor: 6.0, Note: "6,0-fache GKG-Gebühr", NoteEN: "6.0x GKG fee",
			},
		},
		DPMAFees: []DPMAFee{
			{Label: "Patentanmeldung (elektronisch)", LabelEN: "Patent application (electronic)", Fee: 40},
			{Label: "Patentanmeldung (Papier)", LabelEN: "Patent application (paper)", Fee: 60},
			{Label: "Prüfungsantrag", LabelEN: "Examination request", Fee: 350},
			{Label: "Erteilungsgebühr (bis 10 Ansprüche)", LabelEN: "Grant fee (up to 10 claims)", Fee: 125},
			{Label: "je weiterer Anspruch ab Nr. 11", LabelEN: "per additional claim from 11th", Fee: 30},
			{Label: "Gebrauchsmusteranmeldung (elektronisch)", LabelEN: "Utility model application (electronic)", Fee: 30},
			{Label: "Gebrauchsmusteranmeldung (Papier)", LabelEN: "Utility model application (paper)", Fee: 40},
		},
		AnnualFees: []DPMAAnnualFee{
			{Year: 3, Fee: 70}, {Year: 4, Fee: 70}, {Year: 5, Fee: 90},
			{Year: 6, Fee: 130}, {Year: 7, Fee: 180}, {Year: 8, Fee: 240},
			{Year: 9, Fee: 290}, {Year: 10, Fee: 350}, {Year: 11, Fee: 470},
			{Year: 12, Fee: 620}, {Year: 13, Fee: 760}, {Year: 14, Fee: 910},
			{Year: 15, Fee: 1060}, {Year: 16, Fee: 1230}, {Year: 17, Fee: 1410},
			{Year: 18, Fee: 1590}, {Year: 19, Fee: 1790}, {Year: 20, Fee: 2030},
		},
	}

	return resp
}

func handleGebuehrentabellenFeedback(w http.ResponseWriter, r *http.Request) {
	var feedback GebuehrenFeedback
	if err := json.NewDecoder(r.Body).Decode(&feedback); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Ungültige Anfrage."})
		return
	}

	feedback.FeedbackType = strings.TrimSpace(feedback.FeedbackType)
	feedback.Message = strings.TrimSpace(feedback.Message)
	feedback.Schedule = strings.TrimSpace(feedback.Schedule)

	if feedback.Message == "" || feedback.FeedbackType == "" {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Nachricht und Art sind erforderlich."})
		return
	}

	accessToken := ""
	email := ""
	if cookie, err := r.Cookie(auth.SessionCookieName); err == nil {
		accessToken = cookie.Value
		email = extractEmailFromJWT(cookie.Value)
	}

	payload := map[string]string{
		"feedback_type": feedback.FeedbackType,
		"message":       feedback.Message,
		"schedule":      feedback.Schedule,
		"submitted_by":  email,
	}

	jsonBody, err := json.Marshal(payload)
	if err != nil {
		log.Printf("gebuehren feedback marshal error: %v", err)
		writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "Interner Fehler."})
		return
	}

	endpoint := fmt.Sprintf("%s/rest/v1/gebuehrentabellen_feedback", authClient.URL)
	req2, err := http.NewRequest("POST", endpoint, bytes.NewReader(jsonBody))
	if err != nil {
		log.Printf("gebuehren feedback request error: %v", err)
		writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "Interner Fehler."})
		return
	}
	req2.Header.Set("Content-Type", "application/json")
	req2.Header.Set("apikey", authClient.AnonKey)
	if accessToken != "" {
		req2.Header.Set("Authorization", "Bearer "+accessToken)
	} else {
		req2.Header.Set("Authorization", "Bearer "+authClient.AnonKey)
	}
	req2.Header.Set("Prefer", "return=minimal")

	client := &http.Client{Timeout: 5 * time.Second}
	resp, err := client.Do(req2)
	if err != nil {
		log.Printf("gebuehren feedback supabase error: %v", err)
		writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "Fehler beim Speichern."})
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 300 {
		body, _ := io.ReadAll(resp.Body)
		log.Printf("gebuehren feedback supabase status %d: %s", resp.StatusCode, string(body))
		writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "Fehler beim Speichern."})
		return
	}

	writeJSON(w, http.StatusCreated, map[string]string{"ok": "true"})
}
