package database

import (
	"bufio"
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"errors"
	"os"
	"sort"
	"time"
)

var (
	ErrNotFound = errors.New("entry not found")
)

type Database struct {
	path    string
	entries map[string]*Entry
}

func Load(path string) (*Database, error) {
	db := &Database{
		path:    path,
		entries: make(map[string]*Entry),
	}

	file, err := os.Open(path)
	if err != nil {
		if os.IsNotExist(err) {
			return db, nil
		}
		return nil, err
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	// Set larger buffer for potentially long clipboard entries
	buf := make([]byte, 0, 64*1024)
	scanner.Buffer(buf, 1024*1024) // 1MB max line size

	for scanner.Scan() {
		line := scanner.Text()
		if line == "" {
			continue
		}

		var entry Entry
		if err := json.Unmarshal([]byte(line), &entry); err != nil {
			continue
		}

		db.entries[entry.ID] = &entry
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return db, nil
}

func (db *Database) Save() error {
	file, err := os.Create(db.path)
	if err != nil {
		return err
	}
	defer file.Close()

	for _, entry := range db.entries {
		data, err := json.Marshal(entry)
		if err != nil {
			return err
		}
		if _, err := file.Write(data); err != nil {
			return err
		}
		if _, err := file.WriteString("\n"); err != nil {
			return err
		}
	}

	return nil
}

func generateID() string {
	bytes := make([]byte, 2) // 2 bytes = 4 hex chars
	rand.Read(bytes)
	return hex.EncodeToString(bytes)
}

func (db *Database) Add(value string) (*Entry, error) {
	entry := &Entry{
		ID:       generateID(),
		Value:    value,
		DateTime: time.Now(),
	}

	db.entries[entry.ID] = entry
	return entry, db.Save()
}

func (db *Database) Get(id string) (*Entry, error) {
	entry, exists := db.entries[id]
	if !exists {
		return nil, ErrNotFound
	}
	return entry, nil
}

func (db *Database) Delete(id string) error {
	if _, exists := db.entries[id]; !exists {
		return ErrNotFound
	}

	delete(db.entries, id)
	return db.Save()
}

func (db *Database) List(limit int) []*Entry {
	entries := make([]*Entry, 0, len(db.entries))
	for _, entry := range db.entries {
		entries = append(entries, entry)
	}

	// Sort by datetime descending (most recent first)
	sort.Slice(entries, func(i, j int) bool {
		return entries[i].DateTime.After(entries[j].DateTime)
	})

	if limit > 0 && limit < len(entries) {
		entries = entries[:limit]
	}

	return entries
}

func (db *Database) MostRecent() *Entry {
	entries := db.List(1)
	if len(entries) == 0 {
		return nil
	}
	return entries[0]
}

func (db *Database) Count() int {
	return len(db.entries)
}
