package models

import (
	"errors"
	"fmt"
	"time"
	"unicode/utf8"

	"github.com/bluesky-social/indigo/atproto/syntax"
	lexutil "github.com/bluesky-social/indigo/lex/util"
	"tangled.org/core/api/tangled"
)

type String struct {
	Did  syntax.DID
	Rkey syntax.RecordKey
	Cid  *syntax.CID

	// String_File will remain, and we will still use it.
	// We just use `FileContent` when fetching the file.

	// after that, change lexicon, start migrating to blobs
	// when string is migrated, clear FileName and FileContent.
	// when they are cleared, fetch the blob on page load.

	Title       *string
	Description *string
	Files       []String_File
	Created     time.Time
	Edited      *time.Time

	// legacy string data
	FileName    string
	FileContent string

	// optionally, populate this when querying for reverse mappings
	Stats *StringStats
}

// TODO: replace this with [tangled.String_File]
type String_File struct {
	Name string
	Blob *lexutil.LexBlob
	Gzip *GzipInfo
}
type GzipInfo struct {
	MimeType string
	Size     int64
}

func (s *String) AtUri() syntax.ATURI {
	return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", s.Did, tangled.StringNSID, s.Rkey))
}

func (s *String) AsRecord() *tangled.String {
	var description string
	if s.Description != nil {
		description = *s.Description
	}
	return &tangled.String{
		Filename:    s.FileName,
		Description: description,
		Contents:    s.FileContent,
		CreatedAt:   s.Created.Format(time.RFC3339),
	}
}

func (s *String) Validate() error {
	var err error
	if s.FileName == "" && len(s.Files) == 0 {
		err = errors.Join(err, fmt.Errorf("string should have more than one files"))
	}
	// legacy record check
	if utf8.RuneCountInString(s.FileName) > 140 {
		err = errors.Join(err, fmt.Errorf("filename too long"))
	}
	for i, file := range s.Files {
		if utf8.RuneCountInString(file.Name) > 140 {
			err = errors.Join(err, fmt.Errorf("filename too long at files[%d]", i))
		}
	}
	if s.Title != nil {
		if utf8.RuneCountInString(*s.Title) > 140 {
			err = errors.Join(err, fmt.Errorf("title too long"))
		}
	}
	if s.Description != nil {
		if utf8.RuneCountInString(*s.Description) > 280 {
			err = errors.Join(err, fmt.Errorf("description too long"))
		}
	}
	return err
}

func (s String) RenderTitle() string {
	if s.Title != nil {
		return *s.Title
	}
	if len(s.Files) > 0 {
		return s.Files[0].Name
	}
	return s.FileName
}

// FileByName returns first item in files with given filename
func (s *String) FileByName(name string) (String_File, bool) {
	for _, file := range s.Files {
		if file.Name == name {
			return file, true
		}
	}
	return String_File{}, false
}

func (s String) IsLegacySingleFile() bool {
	return len(s.Files) == 0
}

func StringFromRecord(did syntax.DID, rkey syntax.RecordKey, cid syntax.CID, record tangled.String) (String, error) {
	created, err := time.Parse(time.RFC3339, record.CreatedAt)
	if err != nil {
		return String{}, fmt.Errorf("invalid createdAt: %w", err)
	}
	var description *string
	if record.Description != "" {
		description = &record.Description
	}
	return String{
		Did:         did,
		Rkey:        rkey,
		Cid:         &cid,
		Description: description,
		Created:     created,
		FileName:    record.Filename,
		FileContent: record.Contents,
	}, nil
}

type StringStats struct {
	StarCount int
	// CommentCount int
}

type StringFileStats struct {
	LineCount int
	ByteCount int
}
