package server

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

	"github.com/kataras/iris/v12"
	"github.com/rs/zerolog/log"

	"pluja.dev/kycnot.me/config"
	"pluja.dev/kycnot.me/database"
	"pluja.dev/kycnot.me/utils"
)

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

	t := c.URLParam("t")
	q := c.URLParam("q")

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

	filters := []string{"(listed=true && pending=false)"}

	if t != "" && t != "all" {
		filters = append(filters, fmt.Sprintf("(type='%v')", t))
	}

	if q != "" {
		filters = append(
			filters,
			fmt.Sprintf("(name~'%v' || description~'%v' || tags~'%v')", q, q, q),
		)
	}

	if xmr != "" {
		filters = append(filters, fmt.Sprintf("(xmr=%v)", xmr == "on"))
	}
	if btc != "" {
		filters = append(filters, fmt.Sprintf("(btc=%v)", btc == "on"))
	}
	if ln != "" {
		filters = append(filters, fmt.Sprintf("(lightning=%v)", ln == "on"))
	}
	if cash != "" {
		filters = append(filters, fmt.Sprintf("(cash=%v)", cash == "on"))
	}
	if fiat != "" {
		filters = append(filters, fmt.Sprintf("(fiat=%v)", fiat == "on"))
	}

	services, err := database.Pb.GetServices(
		fmt.Sprintf("(%v)", strings.Join(filters, " && ")),
		"-score,@random",
	)

	if err != nil {
		log.Error().Err(err).Msg("Could not get services from Pocketbase")
		c.HTML("<h3>%s</h3>", err.Error())
		return
	}

	announcement := database.Pb.GetAnnouncement()

	c.ViewLayout("main")
	data := iris.Map{
		"Title": "Home",
		"Filters": map[string]string{
			"Type":  t,
			"Query": q,
			"Xmr":   xmr,
			"Btc":   btc,
			"Ln":    ln,
			"Cash":  cash,
			"Fiat":  fiat,
		},
		"Announcement": announcement,
		"Current":      "index",
		"Services":     services,
		"RandomPitch":  nokycPf[rand.Intn(len(nokycPf))],
	}
	if err := c.View("index", data); err != nil {
		c.HTML("<h3>%s</h3>", err.Error())
		return
	}
}

func (s *Server) handleService(c iris.Context) {
	serviceName := strings.ToLower(c.Params().Get("name"))

	service, err := database.Pb.GetServiceByNameOrUrl(serviceName)
	if err != nil {
		log.Error().Err(err).Msgf("Could not get service %v from database", serviceName)
		c.HTML("<h3>%s</h3>", err.Error())
		return
	}

	// TODO: Compute score in background when needed!
	// Update score in background
	//upd := c.URLParam("update", "")
	//if upd == "true" {
	//	go utils.UpdateScore(service)
	//}
	//utils.ComputeScore(service)

	c.ViewLayout("main")
	data := iris.Map{
		"Title":      fmt.Sprintf("%v | Service", serviceName),
		"Service":    service,
		"Attributes": service.Expand["attributes"],
	}
	if err := c.View("service", data); err != nil {
		c.HTML("<h3>%s</h3>", err.Error())
		return
	}
}

func (s *Server) handleScoreSummary(c iris.Context) {
	serviceName := strings.ToLower(c.Params().Get("name"))

	service, err := database.Pb.GetServiceByNameOrUrl(serviceName)
	if err != nil {
		log.Error().Err(err).Msgf("Could not get service %v from database", serviceName)
		c.HTML("<h3>%s</h3>", err.Error())
		return
	}

	_, summary := utils.ComputeScore(service)

	c.Text(summary)
}

func (s *Server) handleAttribute(c iris.Context) {
	attributeId := strings.ToLower(c.Params().Get("id"))

	// Get service from database by name
	attribute, err := database.Pb.GetAttribute(attributeId)
	if err != nil {
		log.Error().Err(err).Msgf("Could not get attribute %v from database", attributeId)
		c.HTML("<h3>%s</h3>", err.Error())
		return
	}

	// Get all services that have this attribute
	services, err := database.Pb.GetServices(
		fmt.Sprintf("(attributes~'%v')", attributeId),
		"-score",
	)
	if err != nil {
		log.Error().Err(err).Msgf("Could not get services with attribute %v from database", attributeId)
		c.HTML("<h3>%s</h3>", err.Error())
		return
	}

	c.ViewLayout("main")
	data := iris.Map{
		"Title":     fmt.Sprintf("%v", attribute.Title),
		"Attribute": attribute,
		"Services":  services,
	}
	if err := c.View("attribute", data); err != nil {
		c.HTML("<h3>%s</h3>", err.Error())
		return
	}
}

func (s *Server) handleRequestServiceForm(c iris.Context) {
	challenge, id, difficulty, err := s.PowChallenger.PowGenerateChallenge(16)
	if err != nil {
		c.HTML("<h3>%s</h3>", err.Error())
		return
	}

	attributes, err := database.Pb.GetAttributes("", "-rating")
	if err != nil {
		log.Error().Err(err).Msg("Could not get attributes from database")
		c.HTML("<h3>%s</h3>", err.Error())
		return
	}

	c.ViewLayout("main")
	data := iris.Map{
		"Title":   "Request a service",
		"Current": "request",
		"Pow": map[string]interface{}{
			"Challenge":  challenge,
			"Difficulty": difficulty,
			"Id":         id,
		},
		"Attributes": attributes,
		"Error":      c.URLParam("error"),
		"Message":    c.URLParam("message"),
	}
	if err := c.View("request_service", data); err != nil {
		c.HTML("<h3>%s</h3>", err.Error())
		return
	}
}

func (s *Server) handleAbout(c iris.Context) {
	c.ViewLayout("main")
	data := iris.Map{
		"Title":   "About",
		"Current": "about",
		"Btc":     config.Conf.Donations.Btc,
		"Xmr":     config.Conf.Donations.Xmr,
		"Lnn":     config.Conf.Donations.Lnn,
	}
	if err := c.View("about", data); err != nil {
		c.HTML("<h3>%s</h3>", err.Error())
		return
	}
}

func (s *Server) handleNostr(c iris.Context) {
	c.ViewLayout("main")
	data := iris.Map{
		"Title": "Nostr",
	}
	if err := c.View("nostr", data); err != nil {
		c.HTML("<h3>%s</h3>", err.Error())
		return
	}
}

func (s *Server) handlePending(c iris.Context) {
	services, err := database.Pb.GetServices("pending=true", "score")
	if err != nil {
		log.Error().Err(err).Msg("Could not get services from database")
		c.HTML("<h3>%s</h3>", err.Error())
		return
	}

	c.ViewLayout("main")
	data := iris.Map{
		"Title":    "Pending",
		"Services": services,
	}
	if err := c.View("pending", data); err != nil {
		c.HTML("<h3>%s</h3>", err.Error())
		return
	}
}

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"`
	PowNonce    string   `form:"pow-nonce"`
	PowId       string   `form:"pow-id"`
}

func (s *Server) handlePostRequestServiceForm(c iris.Context) {
	log.Info().Msg("Handling request service form")
	log.Debug().Msg("Handling request service form")
	var data RequestFormData
	if err := c.ReadForm(&data); err != nil {
		log.Error().Err(err).Msg("Could not parse form data")
		c.Redirect("/request/service?error=Invalid%20Form", iris.StatusSeeOther)
		return
	}

	log.Printf("Nonce: %v, ID: %v", data.PowNonce, data.PowId)
	if !s.PowChallenger.PowVerifyProof(data.PowId, data.PowNonce) {
		log.Error().Msg("Invalid PoW")
		c.Redirect("/request/service?error=Invalid%20Captcha", iris.StatusSeeOther)
		return
	}

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

	if len(data.Attributes) < 1 {
		log.Error().Msgf("Invalid number of attributes: %v", len(data.Attributes))
		c.Redirect("/request/service?error=You%20must%20select%20at%20least%203%20attributes", iris.StatusSeeOther)
		return
	}

	var service database.Service
	// Parse tags
	ts := strings.ReplaceAll(data.Tags, " ", ",")
	// Remove trailing commas
	ts = strings.TrimSuffix(ts, ",")
	// Remove duplicate commas
	ts = strings.ReplaceAll(ts, ",,", ",")
	// Remove leading commas
	ts = strings.TrimPrefix(ts, ",")
	// Convert to lowercase
	ts = strings.ToLower(ts)
	// Create tags array
	tags := strings.Split(ts, ",")

	service.Tags = tags
	service.Name = strings.ToLower(data.Name)
	service.Description = data.Description
	service.LogoURL = utils.UrlParser(data.LogoUrl)
	service.Urls = utils.UrlListParser(data.Urls)
	service.TosUrls = utils.UrlListParser(data.TosUrls)
	service.OnionUrls = utils.UrlListParser(data.OnionUrls)
	service.Xmr = data.Xmr
	service.Btc = data.Btc
	service.Lightning = data.Ln
	service.Fiat = data.Fiat
	service.Cash = data.Cash
	service.Pending = true
	service.Listed = false
	service.Type = data.Type

	if service.Type == "service" {
		service.Category = data.Category
	} else {
		service.Category = ""
	}

	service.KycLevel = data.KYCLevel
	service.Attributes = data.Attributes

	service.Score, _ = utils.ComputeScore(&service)

	// Save service to database
	err := database.Pb.CreateService(service)
	if err != nil {
		log.Error().Err(err).Msg("Could not save service to database")
		c.Redirect("/request/service?error=internal-error", iris.StatusSeeOther)
		return
	}

	// TODO: Update score in background
	// go utils.UpdateScore(sv)
	c.Redirect("/request/service?message=Success!", iris.StatusSeeOther)
}

func (s *Server) handlePgp(c iris.Context) {
	text := `
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Here's my public PGP key:

- -----BEGIN PGP PUBLIC KEY BLOCK-----

mDMEZczexBYJKwYBBAHaRw8BAQdA+X+cNkOv5SmauKLLw+xcEkcDQjVubZ/4Xj3d
5DNvgru0F3BsdWphIDxhZG1pbkBreWNub3QubWU+iJMEExYKADsWIQQEPOrx9H8d
Txv0tFhweQHG2o41CgUCZczexAIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIX
gAAKCRBweQHG2o41CjwVAQDpcxgeXK+2KiVzMkmLCXiAYU0hRhQmA4xwXDkqYxn7
dwD+KzHpcnTwnrK5ZLn3+ti9DVeURTv1lFf9TVZTKgovOAy4OARlzN7EEgorBgEE
AZdVAQUBAQdAvrCLlUmBr/RswvfC4hOGa5Som7CGQp0WzpkyETZmRh0DAQgHiHgE
GBYKACAWIQQEPOrx9H8dTxv0tFhweQHG2o41CgUCZczexAIbDAAKCRBweQHG2o41
ClZ1AQCwZ1XrRYLuM9d5mLXzbZVRjb5INoVXFrZbx9ZkAxKgFAD/dmJI/bUxPcVZ
f6cwP2HYnxD/i2C6ltaLK0SRrxI2XAM=
=Tnvi
- -----END PGP PUBLIC KEY BLOCK-----

Any important matters, will be signed with this key.
-----BEGIN PGP SIGNATURE-----

iHUEARYKAB0WIQQEPOrx9H8dTxv0tFhweQHG2o41CgUCZc0qQwAKCRBweQHG2o41
Ctx3AP9TBHliRKIXpGmfCWKL6sRHqyT/eQQMjONqaZikrcmdzAEA/ff2+nionjTr
mQXRsALQl2Fpw4nY/hIOYkdASomusAg=
=uT9S
-----END PGP SIGNATURE-----

`

	c.Text(text)
}
