package main

import (
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
	"os"

	"github.com/pocketbase/pocketbase/core"
	"github.com/pocketbase/pocketbase/apis"
)

const frontEndDevPort = "5173"
const frontEndProdPort = "3000"

type DeckImportCard struct {
		Name      string 	`json:"name"`
		Amount    int 		`json:"amount"`
}


type DeckImport struct {
		Name       				string 						`json:"name"`
		Url  			 				string 						`json:"url"`
		SelectedBset  		string 						`json:"selected_bset"`
		Commander  			 	string 						`json:"commander_name"`
		Cards 						[]DeckImportCard 	`json:"cards"`
}

func (app *application) SetupRoutes() {
	app.pb.OnServe().BindFunc(func(se *core.ServeEvent) error {
		mode := os.Getenv("GO_ENV")
		path := ""
		if mode == "" {
			log.Fatal("You must the env variable GO_ENV to either 'dev' or 'production'")
		}

		if mode == "dev" {
			path = fmt.Sprintf("http://localhost:%s", frontEndDevPort)
		} else if mode == "production" {
			path = fmt.Sprintf("http://localhost:%s", frontEndProdPort)
		} else {
			log.Fatal(fmt.Sprintf("Error, unrecognized GO_ENV : %s\nSet it to either 'dev' or 'production'", mode))
		}

		target, err := url.Parse(path)
		if err != nil {
			log.Println("[ERROR] failed to parse proxy target URL: %w", err)
		}

		proxy := httputil.NewSingleHostReverseProxy(target)

		proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
			http.Error(w, fmt.Sprintf("proxy error: %v", err), http.StatusBadGateway)
		}

		// Cache Route 
		se.Router.GET("/json/{path...}", func(re *core.RequestEvent) error {
			path := fmt.Sprintf("json/%s",re.Request.PathValue("path"))

			cacheVal := app.pb.Store().Get(path)
			if cacheVal != nil {
				return re.JSON(http.StatusOK, cacheVal)
			} else {
				return re.JSON(http.StatusNotFound, map[string]string{"message": "json not found in cache"})
			}
		})

		// Search API
		se.Router.GET("/api/search", func(re *core.RequestEvent) error {
			searchData := app.pb.Store().Get("searchData").([]CacheCarteSearch)
			search := re.Request.URL.Query().Get("q")

			resultData := filter(searchData, func(card CacheCarteSearch) bool { return strings.Contains(strings.ToLower(card.Name), strings.ToLower(search)) })
			return re.JSON(http.StatusOK, resultData)
		})

		// Create deck API
		se.Router.POST("/api/deck/create", func(re *core.RequestEvent) error {
			authRecord := re.Auth

			data := DeckImport{}
			if err := re.BindBody(&data); err != nil {
				log.Println(err)
				return re.BadRequestError("Failed to read request data", err)
			}
			
			selectedBrawlset := Brawlset{}
			err := app.pb.DB().
					NewQuery(fmt.Sprintf("SELECT id, sets FROM brawlset WHERE sanitized_name = '%v'", data.SelectedBset)).
					One(&selectedBrawlset)
			if err != nil {
				log.Println(err)
				return re.BadRequestError("No brawlsets with this id", err)
			}
		
			possibleCards := []Carte{}
			setsIds := []string{} 
			for _, v := range selectedBrawlset.Sets {
				setsIds = append(setsIds, fmt.Sprintf("'%s'", v))
			}
			query := ""
			if len(selectedBrawlset.Sets) > 1 {
				query = fmt.Sprintf("SELECT id, name, color_identity FROM carte WHERE mtg_set IN (%s)", strings.Join(setsIds, ", "))
			} else {
				query = fmt.Sprintf("SELECT id, name, color_identity FROM carte WHERE mtg_set = %s", setsIds[0])
			}
			err = app.pb.DB().
					NewQuery(query).
					All(&possibleCards)
			if err != nil {
				log.Println(err)
				return re.BadRequestError("Error with cards query", err)
			}

			collection, err := app.pb.FindCollectionByNameOrId("deck")
			if err != nil {
					return err
			}
			record := core.NewRecord(collection)
			record.Set("name", data.Name)
			record.Set("url", data.Url)
			record.Set("brawlset", selectedBrawlset.ID)
			record.Set("owner", authRecord.Id)

			for _, c := range possibleCards {
				if data.Commander == strings.Split(c.Name, " // ")[0] {
					record.Set("commander", c.ID)
					record.Set("color_identity", c.ColorIdentity)
				}
			}

			cardInDeck := []DeckCard{}
			for _, v := range data.Cards {
				cardId := ""
				for _, c := range possibleCards {
					if v.Name == strings.Split(c.Name, " // ")[0] {
						cardId = c.ID
					}
				}
				if cardId == "" {
					log.Println(fmt.Sprintf("Card not found : %s",v.Name))
				}
				cardInDeck = append(cardInDeck, DeckCard{ID: cardId, Amount: v.Amount})
			}
			record.Set("cartes", cardInDeck)

			err = app.pb.Save(record)
			if err != nil {
				log.Println(err)
				re.BadRequestError("Problem with creating deck", err)
			}
			return re.JSON(http.StatusOK, map[string]string{"message": "deck created"})
		}).Bind(apis.RequireAuth())

		// Proxy to svelte app
		se.Router.Any("/{path...}", func(re *core.RequestEvent) error {
			proxy.ServeHTTP(re.Response, re.Request)
			return nil
		})

		return se.Next()
	})
}

func filter(ss []CacheCarteSearch, test func(CacheCarteSearch) bool) (ret []CacheCarteSearch) {
    for _, s := range ss {
        if test(s) {
            ret = append(ret, s)
        }
    }
    return
}
