package main

import (
	"archive/zip"
	"database/sql"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"strings"

	"github.com/gin-gonic/gin"
	_ "github.com/glebarez/go-sqlite"
	log "github.com/sirupsen/logrus"
)

const dbFile = "yyets_sqlite.db"

var (
	buildTime string
	db, _     = sql.Open("sqlite", dbFile)
)

type basicInfo struct {
	Id        int    `json:"id"`
	Cnname    string `json:"cnname"`
	Enname    string `json:"enname"`
	Aliasname string `json:"aliasname"`
}

type Announcement struct {
	Username string `json:"username"`
	Date     string `json:"date"`
	Content  string `json:"content"`
}

func bindataStaticHandler(c *gin.Context) {
	c.Status(http.StatusOK)
	path := c.Request.RequestURI[1:]
	if strings.Contains(path, "search.html") || strings.Contains(path, "resource.html") {
		path = strings.Split(path, "?")[0]
	}
	fmt.Println(path)
	data, err := Asset(path)
	if err != nil {
		// Asset was not found.
		index, _ := Asset("index.html")
		_, _ = c.Writer.Write(index)
	}
	// Write asset

	if strings.Contains(path, ".css") {
		c.Writer.Header().Add("content-type", "text/css")
	} else if strings.Contains(path, ".js") {
		c.Writer.Header().Add("content-type", "text/javascript")
	} else if strings.Contains(path, ".html") {
		c.Writer.Header().Add("content-type", "text/html")
	} else if strings.Contains(path, ".png") {
		c.Writer.Header().Add("content-type", "image/png")
	} else if strings.Contains(path, ".svg") {
		c.Writer.Header().Add("content-type", "image/svg+xml")
	} else if strings.Contains(path, ".jpg") {
		c.Writer.Header().Add("content-type", "image/jpeg")
	} else if strings.Contains(path, ".ico") {
		c.Writer.Header().Add("content-type", "image/x-icon")
	} else if strings.Contains(path, ".gif") {
		c.Writer.Header().Add("content-type", "image/gif")
	}

	_, _ = c.Writer.Write(data)

	// Handle errors here too and cache headers
}

func search(c *gin.Context) {
	keyword, _ := c.GetQuery("keyword")
	keyword = "%" + keyword + "%"

	rows, _ := db.Query("SELECT resource_id, cnname, enname, aliasname FROM yyets "+
		"WHERE cnname LIKE ? or enname LIKE ? or aliasname LIKE ?", keyword, keyword, keyword)
	var searchResult []basicInfo
	for rows.Next() {
		var t basicInfo
		_ = rows.Scan(&t.Id, &t.Cnname, &t.Enname, &t.Aliasname)
		searchResult = append(searchResult, t)
	}
	c.JSON(200, gin.H{
		"data":    searchResult,
		"extra":   []string{},
		"comment": []string{},
	})
}

func resource(c *gin.Context) {
	id, _ := c.GetQuery("id")
	rows, _ := db.Query("SELECT data FROM yyets WHERE resource_id=?", id)
	var result string
	for rows.Next() {
		_ = rows.Scan(&result)
	}
	var data map[string]interface{}
	json.Unmarshal([]byte(result), &data)

	c.JSON(200, gin.H{
		"data": data,
	})
}

func entrance(c *gin.Context) {
	var _, keyword = c.GetQuery("keyword")
	var _, id = c.GetQuery("id")

	if keyword {
		search(c)
	} else if id {
		resource(c)
	} else {
		c.JSON(http.StatusBadRequest, gin.H{
			"message": "Bad request",
		})
	}

}

func announcement(c *gin.Context) {

	c.JSON(http.StatusOK, gin.H{
		"data": []Announcement{{
			Username: "Benny",
			Date:     buildTime,
			Content:  "YYeTs一键运行包 https://yyets.click/。",
		}},
	})
}

func newest(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"data": []string{},
	})
}

func douban(c *gin.Context) {
	var resourceId, _ = c.GetQuery("resource_id")
	var requestType, _ = c.GetQuery("type")

	type Image struct {
		PosterLink string `json:"posterLink"`
	}
	rows, _ := db.Query("SELECT douban,image FROM yyets WHERE resource_id=?", resourceId)
	var doubanInfo string
	var doubanPoster []byte
	for rows.Next() {
		_ = rows.Scan(&doubanInfo, &doubanPoster)
	}
	if doubanInfo == "" {
		var image Image
		log.Warnf("Douban resource not found, requesting to main site ...")
		resp, _ := http.Get("https://yyets.click" + c.Request.URL.String())
		body, _ := io.ReadAll(resp.Body)
		doubanInfo = string(body)
		json.Unmarshal(body, &image)
		resp, _ = http.Get(image.PosterLink)
		body, _ = io.ReadAll(resp.Body)
		_, _ = db.Exec("UPDATE yyets SET douban=?,image=? WHERE resource_id=?", doubanInfo, body, resourceId)
	}

	if requestType == "image" {
		c.Writer.Header().Add("content-type", "image/jpeg")
		_, _ = c.Writer.Write(doubanPoster)
	} else {
		c.Writer.Header().Add("content-type", "application/json")
		c.String(http.StatusOK, doubanInfo)
	}

}

func queryTop(area string) []gin.H {
	var sqlQuery string
	if area == "ALL" {
		sqlQuery = "SELECT resource_id, cnname, enname, aliasname FROM yyets  order by views desc limit 15"
	} else {
		sqlQuery = "SELECT resource_id, cnname, enname, aliasname FROM yyets where area=? order by views desc limit 15"
	}
	rows, _ := db.Query(sqlQuery, area)
	var finaldata []gin.H
	for rows.Next() {
		var t basicInfo
		_ = rows.Scan(&t.Id, &t.Cnname, &t.Enname, &t.Aliasname)
		finaldata = append(finaldata, gin.H{"data": gin.H{"info": t}})
	}
	return finaldata
}

func top(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"ALL": queryTop("ALL"),
		"US":  queryTop("美国"),
		"JP":  queryTop("日本"),
		"KR":  queryTop("韩国"),
		"UK":  queryTop("英国"),
		"class": gin.H{
			"ALL": gin.H{"$regex": ".*"},
			"US":  "美国",
			"JP":  "日本",
			"KR":  "韩国",
			"UK":  "英国",
		},
	})
}

func downloadDB() {
	if _, err := os.Stat(dbFile); err == nil {
		log.Warningln("Database file already exists")
		return
	}

	log.Infoln("Downloading database file...")
	var downloadUrl = "https://pub-74e4c4d2953c4e9e9f32204e472bafed.r2.dev/yyets_sqlite.zip"
	resp, _ := http.Get(downloadUrl)
	file, _ := os.Create("yyets_sqlite.zip")
	_, _ = io.Copy(file, resp.Body)
	_ = resp.Body.Close()
	_ = file.Close()

	log.Infoln("Download complete, extracting...")
	archive, _ := zip.OpenReader("yyets_sqlite.zip")
	for _, f := range archive.File {
		var targetPath = path.Clean(f.Name)
		dstFile, _ := os.OpenFile(targetPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
		fileInArchive, _ := f.Open()
		_, _ = io.Copy(dstFile, fileInArchive)
		_ = dstFile.Close()
		_ = fileInArchive.Close()
	}

	log.Infoln("Extraction complete, cleaning up...")
	_ = os.Remove("yyets_sqlite.zip")
}

func main() {
	downloadDB()
	banner := `
    ▌ ▌ ▌ ▌     ▀▛▘
    ▝▞  ▝▞  ▞▀▖  ▌  ▞▀▘
     ▌   ▌  ▛▀   ▌  ▝▀▖
     ▘   ▘  ▝▀▘  ▘  ▀▀ 
                        
	Lazarus came back from the dead. By @Bennythink
	http://127.0.0.1:8888/
`
	r := gin.Default()
	r.GET("/api/resource", entrance)
	r.GET("/api/announcement", announcement)
	r.GET("/api/comment/newest", newest)
	r.GET("/api/resource/latest", newest)
	r.GET("/api/captcha", newest)
	r.GET("/api/comment", newest)
	r.GET("/api/douban", douban)
	r.GET("/api/top", top)
	r.GET("/js/*f", bindataStaticHandler)
	r.GET("/css/*f", bindataStaticHandler)
	r.GET("/fonts/*f", bindataStaticHandler)
	r.GET("/img/*f", bindataStaticHandler)
	//r.GET("/index.html", bindataStaticHandler)
	//r.GET("/search.html", bindataStaticHandler)
	r.GET("/resource.html", bindataStaticHandler)
	r.GET("/", bindataStaticHandler)
	r.GET("/index.css", bindataStaticHandler)
	r.GET("/svg/*f", bindataStaticHandler)

	r.NoRoute(bindataStaticHandler)

	fmt.Printf(banner)
	_ = r.Run("localhost:8888") // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}
