package internal

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)


type HashService struct {
	lastRecord *LastRecord
}

type LastRecord struct {
	EmisorNIF string
	NumSerie  string
	Fecha     time.Time
	Huella    string
	FechaGen  time.Time
}

func NewHashService() *HashService {
	return &HashService{}
}

func (s *HashService) SetLastRecord(r *LastRecord) {
	s.lastRecord = r
}

func (s *HashService) GetLastRecord() *LastRecord {
	return s.lastRecord
}

func (s *HashService) CalculateHash(data *InvoiceData, previousHash string) string {
	fechaGen := data.FechaGen.Format("2006-01-02T15:04:05-07:00")

	fields := fmt.Sprintf(
		"IDEmisorFactura=%s&NumSerieFactura=%s&FechaExpedicionFactura=%s&TipoFactura=%s&CuotaTotal=%.2f&ImporteTotal=%.2f&Huella=%s&FechaHoraHusoGenRegistro=%s",
		data.EmisorNIF,
		data.NumSerie,
		data.Fecha.Format("02-01-2006"),
		data.TipoFactura,
		data.CuotaTotal,
		data.ImporteTotal,
		previousHash,
		fechaGen,
	)

	hash := sha256.Sum256([]byte(fields))
	return strings.ToUpper(hex.EncodeToString(hash[:]))
}

func (s *HashService) IsFirstRecord() bool {
	return s.lastRecord == nil
}

func (s *HashService) GetPreviousHash() string {
	if s.lastRecord == nil {
		return ""
	}
	return s.lastRecord.Huella
}

type LastRecordStorage interface {
	GetLastRecord(emisorNIF string) (*LastRecord, error)
	SaveLastRecord(r *LastRecord) error
}

type FileLastRecordStorage struct {
	basePath string
}

func NewFileLastRecordStorage(basePath string) *FileLastRecordStorage {
	return &FileLastRecordStorage{basePath: basePath}
}

func (s *FileLastRecordStorage) GetLastRecord(emisorNIF string) (*LastRecord, error) {
	if s.basePath == "" {
		s.basePath = "./data/"
	}

	filePath := filepath.Join(s.basePath, sanitizeFilename(emisorNIF)+".json")
	data, err := os.ReadFile(filePath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("reading last record: %w", err)
	}

	var record LastRecord
	if err := json.Unmarshal(data, &record); err != nil {
		return nil, fmt.Errorf("unmarshaling last record: %w", err)
	}

	return &record, nil
}

func (s *FileLastRecordStorage) SaveLastRecord(r *LastRecord) error {
	if s.basePath == "" {
		s.basePath = "./data/"
	}

	if err := os.MkdirAll(s.basePath, 0750); err != nil {
		return fmt.Errorf("creating directory: %w", err)
	}

	filePath := filepath.Join(s.basePath, sanitizeFilename(r.EmisorNIF)+".json")
	data, err := json.Marshal(r)
	if err != nil {
		return fmt.Errorf("marshaling last record: %w", err)
	}

	if err := os.WriteFile(filePath, data, 0640); err != nil {
		return fmt.Errorf("writing last record: %w", err)
	}

	return nil
}

func sanitizeFilename(name string) string {
	re := regexp.MustCompile(`[^a-zA-Z0-9]`)
	return re.ReplaceAllString(name, "_")
}
