package server

import (
	"context"
	"fmt"
	"math/rand"
	"strings"

	"github.com/gofiber/fiber/v2"
	"github.com/rs/zerolog/log"

	"pluja.dev/kycnot.me/database"
	"pluja.dev/kycnot.me/ent"
	"pluja.dev/kycnot.me/ent/service"
	"pluja.dev/kycnot.me/utils"
)

func (s *Server) handleIndex(c *fiber.Ctx) error {
	nokycPf := []string{
		"KYC-Free Crypto Freedom.",
		"Goodbye KYC, hello privacy.",
		"KYC is a scam. Don't fall for it.",
	}

	t := c.Query("t", "")
	q := c.Query("q", "")

	// Currencies
	xmr := c.Query("xmr", "")
	btc := c.Query("btc", "")
	ln := c.Query("ln", "")
	cash := c.Query("cash", "")
	fiat := c.Query("fiat", "")

	// Query all services from the database and filter them by the query and the currencies
	queryBuilder := database.Client.Service.Query().Where(service.Listed(true), service.Pending(false))

	// Apply the type filter if present
	if t != "" && t != "all" {
		log.Printf("Type: %v", t)
		queryBuilder = queryBuilder.Where(service.TypeEQ(service.Type(t)))
	}

	// Apply the query text filter if present
	if q != "" {
		log.Printf("Query: %v", q)
		queryBuilder = queryBuilder.Where(
			service.Or(
				service.NameContains(q),
				service.DescriptionContains(q),
				service.TagsContains(q),
			),
		)
	}

	// Apply the currency filters if present
	if xmr != "" {
		queryBuilder = queryBuilder.Where(service.XmrEQ(xmr == "on"))
	}
	if btc != "" {
		queryBuilder = queryBuilder.Where(service.BtcEQ(btc == "on"))
	}
	if ln != "" {
		queryBuilder = queryBuilder.Where(service.LightningEQ(ln == "on"))
	}
	if cash != "" {
		queryBuilder = queryBuilder.Where(service.CashEQ(cash == "on"))
	}
	if fiat != "" {
		queryBuilder = queryBuilder.Where(service.FiatEQ(fiat == "on"))
	}

	queryBuilder.Order(ent.Desc(service.FieldScore))

	// Execute the query
	services, err := queryBuilder.All(context.Background())
	if err != nil {
		// Handle error (e.g., log it and return a generic error message to the client)
		return c.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
	}

	return c.Render("index", fiber.Map{
		"Title": "Home",
		"Filters": map[string]string{
			"Type":  t,
			"Query": q,
			"Xmr":   xmr,
			"Btc":   btc,
			"Ln":    ln,
			"Cash":  cash,
			"Fiat":  fiat,
		},
		"Current":     "index",
		"Services":    services,
		"RandomPitch": nokycPf[rand.Intn(len(nokycPf))],
	})
}

func (s *Server) handleService(c *fiber.Ctx) error {
	serviceName := strings.ToLower(c.Params("name"))

	// Get service from database by name
	service, err := database.Client.Service.Query().Where(service.NameEQ(serviceName)).First(context.Background())
	if err != nil {
		log.Error().Err(err).Msgf("Could not get service %v from database", serviceName)
		return c.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
	}

	// TESTING
	//log.Printf("ToS: %v", service.TosHighlights)
	//if service.TosHighlights == nil {
	//	tos, err := tos_scraper.GetBodyHtml(service.TosUrls[0])
	//	if err != nil {
	//		log.Error().Err(err).Msg("Could not scrape ToS")
	//	}
	//	highlights, err := ai.GetTosHighlights(tos)
	//	if err != nil {
	//		log.Error().Err(err).Msg("Could not parse ToS")
	//	}
	//	log.Printf("%+v", highlights)
	//
	//	if service.TosHighlights == nil {
	//		database.Client.Service.UpdateOne(service).SetTosHighlights(&highlights).Save(context.Background())
	//	}
	//	service.TosHighlights = &highlights
	//}

	// Update score in background
	upd := c.Query("update", "")
	if upd == "true" {
		go utils.UpdateScore(service)
	}

	attributes := database.ServiceAttributes.GetAttributesFromList(service.Attributes)

	utils.ComputeScore(service)
	log.Printf("Service: %v", serviceName)
	return c.Render("service", fiber.Map{
		"Title":      fmt.Sprintf("%v | Service", serviceName),
		"Service":    service,
		"Attributes": attributes,
	})
}

func (s *Server) handleAttribute(c *fiber.Ctx) error {
	attributeId := strings.ToLower(c.Params("id"))

	// Get service from database by name
	attribute := database.ServiceAttributes.GetAttribute(attributeId)

	// Get all services that have this attribute
	services := database.Client.Service.Query().Where(service.AttributesContains(attributeId)).Where(service.Pending(false), service.Listed(true)).AllX(context.Background())

	return c.Render("attribute", fiber.Map{
		"Title":     fmt.Sprintf("%v | Attribute", attribute.ID),
		"Attribute": attribute,
		"Services":  services,
	})
}

func (s *Server) handleRequestServiceForm(c *fiber.Ctx) error {
	challenge, id, difficulty, err := s.PowChallenger.PowGenerateChallenge(16)
	if err != nil {
		return err
	}

	reverse := true
	return c.Render("request_service", fiber.Map{
		"Title":   "Request a service",
		"Current": "request",
		"Pow": fiber.Map{
			"Challenge":  challenge,
			"Difficulty": difficulty,
			"Id":         id,
		},
		"Attributes": database.ServiceAttributes.GetSortedAttributes(reverse),
		"Error":      c.Query("error", ""),
	})
}

type RequestFormData struct {
	Type        string   `form:"type"`
	Category    string   `form:"category"`
	Name        string   `form:"name"`
	Description string   `form:"description"`
	Urls        string   `form:"urls"`
	LogoUrl     string   `form:"logo_url"`
	TosUrls     string   `form:"tos_urls"`
	OnionUrls   string   `form:"onion_urls"`
	Tags        string   `form:"tags"`
	Xmr         bool     `form:"xmr"`
	Btc         bool     `form:"btc"`
	Ln          bool     `form:"ln"`
	Fiat        bool     `form:"fiat"`
	Cash        bool     `form:"cash"`
	KYCLevel    int      `form:"kyc_level"`
	Attributes  []string `form:"attributes"`
}

func (s *Server) handleRequestServicePostForm(c *fiber.Ctx) error {
	nonce, id := c.FormValue("pow-nonce"), c.FormValue("pow-id")
	log.Printf("Nonce: %v, ID: %v", nonce, id)
	if !s.PowChallenger.PowVerifyProof(id, nonce) {
		return c.Redirect("/request/service?error=invalid-captcha")
	}

	data := new(RequestFormData)
	if err := c.BodyParser(data); err != nil {
		log.Error().Err(err).Msg("Could not parse form data")
		return c.Redirect("/request/service?error=invalid-form")
	}

	if data.KYCLevel < 0 || data.KYCLevel >= 4 {
		log.Error().Msgf("Invalid KYC Level value: %v", c.FormValue("kyc_level"))
		return c.Redirect("/request/service?error=invalid-kyc-level")
	}

	// Parse tags
	tags := strings.ReplaceAll(data.Tags, " ", ",")
	// Remove trailing commas
	tags = strings.TrimSuffix(tags, ",")
	// Remove duplicate commas
	tags = strings.ReplaceAll(tags, ",,", ",")
	// Remove leading commas
	tags = strings.TrimPrefix(tags, ",")
	// Convert to lowercase
	tags = strings.ToLower(tags)

	log.Printf("%+v", data)

	sv, err := database.Client.Service.Create().
		SetName(strings.ToLower(data.Name)).
		SetDescription(data.Description).
		SetType(service.Type(data.Type)).
		SetLogoURL(utils.UrlParser(data.LogoUrl)).
		SetUrls(utils.UrlListParser(data.Urls)).
		SetTosUrls(utils.UrlListParser(data.TosUrls)).
		SetOnionUrls(utils.UrlListParser(data.OnionUrls)).
		SetTags(tags).
		SetXmr(data.Xmr).
		SetBtc(data.Btc).
		SetLightning(data.Ln).
		SetFiat(data.Fiat).
		SetCash(data.Cash).
		SetKycLevel(data.KYCLevel).
		SetCategory(data.Category).
		SetAttributes(strings.Join(data.Attributes, ",")).
		SetPending(false).
		SetListed(true).
		Save(c.Context())
	if err != nil {
		log.Error().Err(err).Msg("Could not save service to database")
		return c.Redirect("/request/service?error=internal-error")
	}

	go utils.UpdateScore(sv)
	return c.Redirect("/request/service?message=success")
}
