package routes

import (
	"archive/zip"
	"bytes"
	"encoding/json"
	"encoding/xml"
	"errors"
	"frostfire.io/dir2opdspse/app/opds"
	"frostfire.io/dir2opdspse/app/utils"
	"github.com/gorilla/mux"
	"github.com/urfave/cli/v2"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"regexp"
	"slices"
	"strconv"
	"time"
)

func Init(c *cli.Context, r *mux.Router) {
	r.HandleFunc("/", Home).Methods("GET")
	r.HandleFunc("/stream/{manga}/{page}", Stream).Methods("GET")
	r.HandleFunc("/test/{path:.*}/{page}", Test).Methods("GET")
}

func Home(w http.ResponseWriter, r *http.Request) {
	feed := opds.FeedBuilder.
		ID("dir2opds").
		Title("dir2OPDS").
		Updated(time.Now()).
		AddLink(opds.LinkBuilder.
			Rel("start").
			Href("/").
			Type("application/atom+xml;profile=opds-catalog;kind=navigation").
			Build())

	dirEntries, _ := os.ReadDir("books")
	for _, entry := range dirEntries {
		if !entry.IsDir() {
			zFile, _ := zip.OpenReader(path.Join("books", entry.Name()))
			//zCount := len(zFile.File)
			_ = zFile.Close()

			feed = feed.AddEntry(opds.EntryBuilder.
				ID(utils.FileBasename(entry.Name())).
				Title(utils.FileBasename(entry.Name())).
				AddLink(opds.LinkBuilder.
					Rel("http://opds-spec.org/acquisition").
					Href("/manga/" + entry.Name()).
					Type("application/x-cbz").
					Title(utils.FileBasename(entry.Name())).
					Build()).
				AddLink(opds.LinkBuilder.
					Rel("http://opds-spec.org/image").
					Href("/stream/" + entry.Name() + "/0").
					Type("image/png").
					Build()).
				AddLink(opds.LinkBuilder.
					Rel("http://opds-spec.org/image/thumbnail").
					Href("/stream/" + entry.Name() + "/0").
					Type("image/png").
					Build()).
				AddLink(opds.LinkBuilder.
					Rel("http://vaemendis.net/opds-pse/stream").
					Href("/stream/" + entry.Name() + "/{pageNumber}").
					Type("image/png").
					//PseCount(10).
					Build()).
				Build())
		}
	}
	data, err := xml.MarshalIndent(feed.Build(), "", "  ")
	if err != nil {
		panic(err)
	}
	http.ServeContent(w, r, "feed.xml", time.Now(), bytes.NewReader(data))
}

func Stream(w http.ResponseWriter, r *http.Request) {
	re := regexp.MustCompile(`(\d+)(.*)`)
	vars := mux.Vars(r)
	if len(re.FindStringIndex(vars["page"])) <= 0 {
		w.WriteHeader(404)
		return
	}

	pageNumber := re.FindStringSubmatch(vars["page"])[1]

	mangaPath := path.Join("books", vars["manga"])

	if _, err := os.Stat(mangaPath); errors.Is(err, os.ErrNotExist) {
		w.WriteHeader(404)
		return
	}

	zf, err := zip.OpenReader(mangaPath)
	if err != nil {
		log.Fatal(err)
	}

	pageIndex := slices.IndexFunc(zf.File, func(f *zip.File) bool {
		page, _ := strconv.Atoi(pageNumber)
		return f.Name == (strconv.Itoa(page+1) + ".png")
	})

	if pageIndex == -1 {
		w.WriteHeader(404)
		return
	}

	imf, _ := zf.File[pageIndex].Open()
	imageBytes, _ := io.ReadAll(imf)
	_ = imf.Close()
	_ = zf.Close()

	http.ServeContent(w, r, zf.File[0].Name, time.Now(), bytes.NewReader(imageBytes))
}

func Test(w http.ResponseWriter, r *http.Request) {
	data, _ := json.MarshalIndent(r.URL, "", "  ")
	http.ServeContent(w, r, "data.json", time.Now(), bytes.NewReader(data))
}
