package model

import (
	"crawler/config"
	"crawler/service"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
)

type G2AProductModel struct {
	G2AProductParams
	G2AProductResult
	G2AProduct
}

// 검색 변수들
type G2AProductParams struct {
	Page              int    `json:"page"`
	ID                string `json:"id"`
	MinQty            int    `json:"minQty"`
	MinPriceFrom      int    `json:"minPriceFrom"`
	MinPriceTo        int    `json:"minPriceTo"`
	IncludeOutOfStock string `json:"includeOutOfStock"`
	UpdatedAtFrom     string `json:"updatedAtFrom"`
	UpdatedAtTo       string `json:"updatedAtTo"`
}

// G2A 상품 조회 결과
type G2AProductResult struct {
	Total int          `json:"total"`
	Page  int          `json:"page"`
	Docs  []G2AProduct `json:"docs"`
}

// G2A 상품 정보
type G2AProduct struct {
	ID                 string `json:"id"`
	Name               string `json:"name"`
	IsTest             int
	Type               string       `json:"type"`
	Slug               string       `json:"slug"`
	Qty                int          `json:"qty"`
	MinPrice           float64      `json:"minPrice"`
	RetailMinPrice     float64      `json:"retail_min_price"`
	RetailMinBasePrice float64      `json:"retailMinBasePrice"`
	Thumbnail          string       `json:"thumbnail"`
	SmallImage         string       `json:"smallImage"`
	CoverImage         string       `json:"coverImage"`
	Images             []string     `json:"images"`
	UpdatedAt          string       `json:"updated_at"`
	ReleaseDate        string       `json:"release_date"`
	Region             string       `json:"region"`
	Developer          string       `json:"developer"`
	Publisher          string       `json:"publisher"`
	Platform           string       `json:"platform"`
	PriceLimit         PriceLimit   `json:"priceLimit"`
	Restrictions       Restrictions `json:"restrictions"`
	Requirements       Requirements `json:"requirements"`
	Videos             []Videos     `json:"videos"`
	Categories         []Categories `json:"categories"`
}

type PriceLimit struct {
	Min float64 `json:"min"`
	Max float64 `json:"max"`
}

type Restrictions struct {
	PegiViolence       bool `json:"pegi_violence"`
	PegiProfanity      bool `json:"pegi_profanity"`
	PegiDiscrimination bool `json:"pegi_discrimination"`
	PegiDrugs          bool `json:"pegi_drugs"`
	PegiFear           bool `json:"pegi_fear"`
	PegiGambling       bool `json:"pegi_gambling"`
	PegiOnline         bool `json:"pegi_online"`
	PegiSex            bool `json:"pegi_sex"`
}

type Requirements struct {
	Minimal     Spec `json:"minimal"`
	Recommended Spec `json:"recommended"`
}

type Spec struct {
	ReqProcessor string `json:"reqprocessor"`
	ReqGraphics  string `json:"reqgraphics"`
	ReqMemory    string `json:"reqmemory"`
	ReqDiskSpace string `json:"reqdiskspace"`
	ReqSystem    string `json:"reqsystem"`
	ReqOther     string `json:"reqother"`
}

type Videos struct {
	Type string `json:"type"`
	Url  string `json:"url"`
}

type Categories struct {
	ID   int    `json:"id"`
	Name string `json:"name"`
}

func (this *G2AProductModel) IsExists(id string) bool {
	var (
		db     = service.DB_PLAYR
		conn   = db.SQLDB
		query  = "SELECT IF(COUNT(*) <= 0, 0, 1) AS `exists` FROM tb_g2a_product WHERE id = ?;"
		exists = false
	)

	err := conn.QueryRow(query, id).Scan(&exists)
	if err != nil {
		db.SetErrorLog(err, query)
		return exists
	}

	db.SetGeneralLog(config.GL_ACTION_SELECT, query, "select exists g2a")
	return exists
}

func (this *G2AProductModel) Insert(list []G2AProduct) error {
	if len(list) == 0 {
		return fmt.Errorf("Product 상품이 존재하지 않습니다.")
	}

	var (
		db    = service.DB_PLAYR
		conn  = db.SQLDB
		query = `
			INSERT INTO tb_g2a_product (
				id, name, is_test, type, slug, qty, min_price, retail_min_price, retail_min_base_price,
				thumbnail, small_image, cover_image, images, updated_at, release_date, region, developer,
				publisher, platform, price_limit, restrictions, requirements, videos, categories, created_at
			)
			VALUES 
		`
		vals = []interface{}{}
		dup  string
	)

	for _, row := range list {
		query += `(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW()),`

		data := this.DataFilter(row)

		vals = append(vals,
			data["id"], data["name"], data["IsTest"], data["type"], data["slug"], data["qty"],
			data["minPrice"], data["retail_min_price"], data["retailMinBasePrice"], data["thumbnail"],
			data["smallImage"], data["coverImage"], data["images"], data["updated_at"], data["release_date"],
			data["region"], data["developer"], data["publisher"], data["platform"], data["priceLimit"],
			data["restrictions"], data["requirements"], data["videos"], data["categories"],
		)

		dup += `name = VALUES(name), is_test = VALUES(is_test), type = VALUES(type), 
				slug = VALUES(slug), qty = VALUES(qty), min_price = VALUES(min_price), retail_min_price = VALUES(retail_min_price), 
				retail_min_base_price = VALUES(retail_min_base_price), thumbnail = VALUES(thumbnail), small_image = VALUES(small_image), 
				cover_image = VALUES(cover_image), images = VALUES(images), updated_at = VALUES(updated_at), release_date = VALUES(release_date), 
				region = VALUES(region), developer = VALUES(developer), publisher = VALUES(publisher),
				platform = VALUES(platform), price_limit = VALUES(price_limit), restrictions = VALUES(restrictions), 
				requirements = VALUES(requirements), videos = VALUES(videos), categories = VALUES(categories),`

	}

	query = query[0 : len(query)-1]
	dup = dup[0 : len(dup)-1]

	query += `ON DUPLICATE KEY UPDATE ` + dup

	stmt, err := conn.Prepare(query)
	if err != nil {
		db.SetErrorLog(err, query)
		return err
	}
	defer stmt.Close()

	_, err = stmt.Exec(vals...)
	if err != nil {
		db.SetErrorLog(err, query)
		return err
	}

	db.SetGeneralLog(config.GL_ACTION_WRITE, query, "insert g2a products")
	return nil
}

func (this *G2AProductModel) Update(row G2AProduct) error {
	var (
		db    = service.DB_PLAYR
		conn  = db.SQLDB
		query = `
			UPDATE tb_g2a_product SET 
				name = ?, is_test = ?, type = ?, slug = ?, qty = ?, min_price = ?, retail_min_price = ?, retail_min_base_price = ?,
				thumbnail = ?, small_image = ?, cover_image = ?, images = ?, updated_at = ?, release_date = ?, region = ?, developer = ?,
				publisher = ?, platform = ?, price_limit = ?, restrictions = ?, requirements = ?, videos = ?, categories = ?, created_at = NOW()
			WHERE id = ?;
		`
		data = this.DataFilter(row)
	)

	_, err := conn.Exec(query,
		data["name"], data["IsTest"], data["type"], data["slug"], data["qty"],
		data["minPrice"], data["retail_min_price"], data["retailMinBasePrice"], data["thumbnail"],
		data["smallImage"], data["coverImage"], data["images"], data["updated_at"], data["release_date"],
		data["region"], data["developer"], data["publisher"], data["platform"], data["priceLimit"],
		data["restrictions"], data["requirements"], data["videos"], data["categories"], data["id"])

	if err != nil {
		db.SetErrorLog(err, query)
		return err
	}

	db.SetGeneralLog(config.GL_ACTION_MODIFY, query, "update g2a product info")
	return nil
}

func (this *G2AProductModel) Info(id int) (G2AProduct, error) {
	var (
		db    = service.DB_PLAYR
		conn  = db.SQLDB
		query = `SELECT id, name, qty, min_price, retail_min_price, retail_min_base_price FROM tb_g2a_product WHERE id = ?;`
		info  G2AProduct
	)

	err := conn.QueryRow(query, id).Scan(
		&info.ID, &info.Name, &info.Qty, &info.MinPrice, &info.RetailMinPrice, &info.RetailMinBasePrice,
	)

	if err != nil && err != sql.ErrNoRows {
		db.SetErrorLog(err, query)
		return info, err
	}

	db.SetGeneralLog(config.GL_ACTION_SELECT, query, "select g2a product info")
	return info, nil
}

func (this *G2AProductModel) DataFilter(product G2AProduct) map[string]interface{} {
	var row = map[string]any{}
	data, _ := json.Marshal(product)
	_ = json.Unmarshal(data, &row)

	for k, v := range row {
		switch c := v.(type) {
		case string:
			if c == "" {
				row[k] = nil
			}
		case int, int32, int64:
			if c == 0 {
				row[k] = 0
			}
		case float32, float64:
			if c == 0 || c == nil || c == "" {
				row[k] = 0.0
			}
		case []string:
			if len(c) == 0 {
				row[k] = nil
			} else {
				s, _ := json.Marshal(c)
				row[k] = string(s)
			}
		case nil:
			row[k] = nil
		case []interface{}:
			if c == nil || len(c) == 0 {
				row[k] = nil
			} else {
				s, _ := json.Marshal(c)
				row[k] = string(s)
			}
		case map[string]interface{}:
			if c == nil || len(c) == 0 {
				row[k] = nil
			} else {
				s, _ := json.Marshal(c)
				row[k] = string(s)
			}
		default:
			fmt.Println(k)
			fmt.Println(reflect.TypeOf(c))
		}
	}

	return row
}
