// Copyright (C) 2025 wangyusong
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

package model

import (
	"bytes"
	"encoding/json"
	"fmt"
	"sort"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/glidea/zenfeed/pkg/util/buffer"
)

const (
	AppName = "zenfeed"
)

// LabelXXX is the metadata label for the feed.
const (
	LabelType    = "type"
	LabelSource  = "source"
	LabelTitle   = "title"
	LabelLink    = "link"
	LabelPubTime = "pub_time"
	LabelContent = "content"
)

// Feed is core data model for a feed.
//
//	E.g. {
//	 "labels": {
//	   "title": "The most awesome feed management software of 2025 has been born",
//	   "content": "....",
//	   "link": "....",
//	 },
//	 "time": "2025-01-01T00:00:00Z",
//	}
type Feed struct {
	ID     uint64    `json:"-"`
	Labels Labels    `json:"labels"`
	Time   time.Time `json:"time"`
}

func (f *Feed) Validate() error {
	if len(f.Labels) == 0 {
		return errors.New("labels is required")
	}
	for i := range f.Labels {
		l := &f.Labels[i]
		if l.Key == "" {
			return errors.New("label key is required")
		}
	}
	if f.Time.IsZero() {
		f.Time = time.Now()
	}

	return nil
}

type Labels []Label

func (ls *Labels) FromMap(m map[string]string) {
	*ls = make(Labels, 0, len(m))
	for k, v := range m {
		*ls = append(*ls, Label{Key: k, Value: v})
	}
}

func (ls Labels) Map() map[string]string {
	m := make(map[string]string, len(ls))
	for _, l := range ls {
		m[l.Key] = l.Value
	}

	return m
}

func (ls Labels) String() string {
	ls.EnsureSorted()
	var b strings.Builder
	for i, l := range ls {
		b.WriteString(l.Key)
		b.WriteString(": ")
		b.WriteString(l.Value)
		if i < len(ls)-1 {
			b.WriteString(",")
		}
	}

	return b.String()
}

func (ls Labels) Get(key string) string {
	for _, l := range ls {
		if l.Key != key {
			continue
		}

		return l.Value
	}

	return ""
}

func (ls *Labels) Put(key, value string, sort bool) {
	for i, l := range *ls {
		if l.Key != key {
			continue
		}
		(*ls)[i].Value = value

		return
	}
	*ls = append(*ls, Label{Key: key, Value: value})
	if sort {
		ls.EnsureSorted()
	}
}

func (ls Labels) MarshalJSON() ([]byte, error) {
	ls.EnsureSorted()

	buf := buffer.Get()
	defer buffer.Put(buf)

	if _, err := buf.WriteString("{"); err != nil {
		return nil, errors.Wrap(err, "write starting brace for Labels object")
	}

	for i, l := range ls {
		if _, err := fmt.Fprintf(buf, "\"%s\":", l.Key); err != nil {
			return nil, errors.Wrap(err, "write label key")
		}

		escapedVal, err := json.Marshal(l.Value)
		if err != nil {
			return nil, errors.Wrap(err, "marshal label value")
		}
		if _, err := buf.Write(escapedVal); err != nil {
			return nil, errors.Wrap(err, "write label value")
		}

		if last := i == len(ls)-1; !last {
			if _, err := buf.WriteString(","); err != nil {
				return nil, errors.Wrap(err, "write comma for Labels object")
			}
		}
	}

	if _, err := buf.WriteString("}"); err != nil {
		return nil, errors.Wrap(err, "write ending brace for Labels object")
	}

	return buf.Bytes(), nil
}

func (ls *Labels) UnmarshalJSON(data []byte) error {
	dec := json.NewDecoder(bytes.NewReader(data))

	// Expect starting '{'
	if err := readExpectedDelim(dec, '{'); err != nil {
		return errors.Wrap(err, "read starting brace for Labels object")
	}

	// Read key-value pairs.
	var labels Labels
	for dec.More() {
		key, value, err := readKeyValue(dec)
		if err != nil {
			return errors.Wrapf(err, "read key-value pair for Labels object")
		}

		labels = append(labels, Label{Key: key, Value: value})
	}

	// Expect starting '}'
	if err := readExpectedDelim(dec, '}'); err != nil {
		return errors.Wrap(err, "read ending brace for Labels object")
	}

	// Ensure sorted.
	*ls = labels
	ls.EnsureSorted()

	return nil
}

func (ls Labels) EnsureSorted() {
	if !ls.sorted() {
		ls.sort()
	}
}

func (ls Labels) sorted() bool {
	sorted := true
	for i := range len(ls) - 1 {
		if ls[i].Key > ls[i+1].Key {
			sorted = false

			break
		}
	}

	return sorted
}

func (ls Labels) sort() {
	sort.Slice(ls, func(i, j int) bool {
		return ls[i].Key < ls[j].Key
	})
}

type Label struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}

// readExpectedDelim reads the next token and checks if it's the expected delimiter.
func readExpectedDelim(dec *json.Decoder, expected json.Delim) error {
	t, err := dec.Token()
	if err != nil {
		return errors.Wrapf(err, "read token")
	}

	delim, ok := t.(json.Delim)
	if !ok || delim != expected {
		return errors.Errorf("expected '%c' delimiter, got %T %v", expected, t, t)
	}

	return nil
}

// readKeyValue reads a single key-value pair from the JSON object.
// Assumes the key is a string and the value decodes into a string.
func readKeyValue(dec *json.Decoder) (key string, value string, err error) {
	// Read key.
	keyToken, err := dec.Token()
	if err != nil {
		return "", "", errors.Wrap(err, "read key token")
	}
	keyStr, ok := keyToken.(string)
	if !ok {
		return "", "", errors.Errorf("expected string key, got %T %v", keyToken, keyToken)
	}

	// Read value.
	var valStr string
	if err := dec.Decode(&valStr); err != nil {
		return "", "", errors.Wrapf(err, "decode value for key %q", keyStr)
	}

	return keyStr, valStr, nil
}
