package storage

import (
	"crm-go/models"
	"crypto/rand"
	"encoding/hex"
	"encoding/json"
	"os"
	"path/filepath"
	"sync"
	"time"
)

type TrialPeriodStorage interface {
	GetAllTrialPeriods() ([]models.TrialPeriod, error)
	GetTrialPeriodsByCustomerID(customerID string) ([]models.TrialPeriod, error)
	GetTrialPeriodByID(id string) (*models.TrialPeriod, error)
	CreateTrialPeriod(trialPeriod models.TrialPeriod) (*models.TrialPeriod, error)
	UpdateTrialPeriod(id string, updates models.UpdateTrialPeriodRequest) error
	DeleteTrialPeriod(id string) error
}

type trialPeriodStorage struct {
	filePath string
	mutex    sync.RWMutex
}

func NewTrialPeriodStorage(filePath string) TrialPeriodStorage {
	storage := &trialPeriodStorage{
		filePath: filePath,
	}
	return storage
}

func (ts *trialPeriodStorage) GetAllTrialPeriods() ([]models.TrialPeriod, error) {
	ts.mutex.RLock()
	defer ts.mutex.RUnlock()

	trialPeriods, err := ts.loadTrialPeriods()
	if err != nil {
		return nil, err
	}

	// Sort by CreatedAt in descending order (newest first)
	for i := 0; i < len(trialPeriods)-1; i++ {
		for j := i + 1; j < len(trialPeriods); j++ {
			if trialPeriods[i].CreatedAt.Before(trialPeriods[j].CreatedAt) {
				trialPeriods[i], trialPeriods[j] = trialPeriods[j], trialPeriods[i]
			}
		}
	}

	return trialPeriods, nil
}

func (ts *trialPeriodStorage) GetTrialPeriodsByCustomerID(customerID string) ([]models.TrialPeriod, error) {
	ts.mutex.RLock()
	defer ts.mutex.RUnlock()

	allPeriods, err := ts.loadTrialPeriods()
	if err != nil {
		return nil, err
	}

	var customerPeriods []models.TrialPeriod
	for _, period := range allPeriods {
		if period.CustomerName == customerID {
			customerPeriods = append(customerPeriods, period)
		}
	}

	// Sort by EndTime in descending order (latest first)
	for i := 0; i < len(customerPeriods)-1; i++ {
		for j := i + 1; j < len(customerPeriods); j++ {
			if customerPeriods[i].EndTime.Before(customerPeriods[j].EndTime) {
				customerPeriods[i], customerPeriods[j] = customerPeriods[j], customerPeriods[i]
			}
		}
	}

	return customerPeriods, nil
}

func (ts *trialPeriodStorage) GetTrialPeriodByID(id string) (*models.TrialPeriod, error) {
	ts.mutex.RLock()
	defer ts.mutex.RUnlock()

	trialPeriods, err := ts.loadTrialPeriods()
	if err != nil {
		return nil, err
	}

	for _, period := range trialPeriods {
		if period.ID == id {
			return &period, nil
		}
	}

	return nil, nil
}

func (ts *trialPeriodStorage) CreateTrialPeriod(trialPeriod models.TrialPeriod) (*models.TrialPeriod, error) {
	ts.mutex.Lock()
	defer ts.mutex.Unlock()

	if trialPeriod.ID == "" {
		trialPeriod.ID = generateTrialPeriodUUID()
	}

	if trialPeriod.CreatedAt.IsZero() {
		trialPeriod.CreatedAt = time.Now()
	}

	trialPeriods, err := ts.loadTrialPeriods()
	if err != nil {
		return nil, err
	}

	trialPeriods = append(trialPeriods, trialPeriod)

	if err := ts.saveTrialPeriods(trialPeriods); err != nil {
		return nil, err
	}

	return &trialPeriod, nil
}

func (ts *trialPeriodStorage) UpdateTrialPeriod(id string, updates models.UpdateTrialPeriodRequest) error {
	ts.mutex.Lock()
	defer ts.mutex.Unlock()

	trialPeriods, err := ts.loadTrialPeriods()
	if err != nil {
		return err
	}

	for i, period := range trialPeriods {
		if period.ID == id {
			if updates.Source != nil {
				trialPeriods[i].Source = *updates.Source
			}
			if updates.StartTime != nil {
				startTime, err := time.Parse(time.RFC3339, *updates.StartTime)
				if err == nil {
					trialPeriods[i].StartTime = startTime
				}
			}
			if updates.EndTime != nil {
				endTime, err := time.Parse(time.RFC3339, *updates.EndTime)
				if err == nil {
					trialPeriods[i].EndTime = endTime
				}
			}
			if updates.IsTrial != nil {
				trialPeriods[i].IsTrial = *updates.IsTrial
			}

			return ts.saveTrialPeriods(trialPeriods)
		}
	}

	return nil
}

func (ts *trialPeriodStorage) DeleteTrialPeriod(id string) error {
	ts.mutex.Lock()
	defer ts.mutex.Unlock()

	trialPeriods, err := ts.loadTrialPeriods()
	if err != nil {
		return err
	}

	for i, period := range trialPeriods {
		if period.ID == id {
			trialPeriods = append(trialPeriods[:i], trialPeriods[i+1:]...)
			return ts.saveTrialPeriods(trialPeriods)
		}
	}

	return nil
}

func (ts *trialPeriodStorage) saveTrialPeriods(trialPeriods []models.TrialPeriod) error {
	dir := filepath.Dir(ts.filePath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}

	data, err := json.MarshalIndent(trialPeriods, "", "  ")
	if err != nil {
		return err
	}

	return os.WriteFile(ts.filePath, data, 0644)
}

func (ts *trialPeriodStorage) loadTrialPeriods() ([]models.TrialPeriod, error) {
	if _, err := os.Stat(ts.filePath); os.IsNotExist(err) {
		return []models.TrialPeriod{}, nil
	}

	data, err := os.ReadFile(ts.filePath)
	if err != nil {
		return nil, err
	}

	var trialPeriods []models.TrialPeriod
	if err := json.Unmarshal(data, &trialPeriods); err != nil {
		return nil, err
	}

	return trialPeriods, nil
}

func generateTrialPeriodUUID() string {
	bytes := make([]byte, 16)
	rand.Read(bytes)
	bytes[6] = (bytes[6] & 0x0f) | 0x40
	bytes[8] = (bytes[8] & 0x3f) | 0x80
	return hex.EncodeToString(bytes)
}
