package storage

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

type FollowUpStorage interface {
	GetAllFollowUps() ([]models.FollowUp, error)
	GetFollowUpByID(id string) (*models.FollowUp, error)
	CreateFollowUp(followUp models.FollowUp) error
	UpdateFollowUp(id string, updates models.UpdateFollowUpRequest) error
	DeleteFollowUp(id string) error
	SaveFollowUps(followUps []models.FollowUp) error
	LoadFollowUps() ([]models.FollowUp, error)
	GetPendingNotifications() ([]models.FollowUp, error)
	MarkNotificationSent(id string) error
}

type followUpStorage struct {
	filePath string
	mutex    sync.RWMutex
}

func NewFollowUpStorage(filePath string) FollowUpStorage {
	storage := &followUpStorage{
		filePath: filePath,
	}
	return storage
}

func (fs *followUpStorage) GetAllFollowUps() ([]models.FollowUp, error) {
	fs.mutex.RLock()
	defer fs.mutex.RUnlock()

	followUps, err := fs.LoadFollowUps()
	if err != nil {
		return nil, err
	}

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

	return followUps, nil
}

func (fs *followUpStorage) GetFollowUpByID(id string) (*models.FollowUp, error) {
	fs.mutex.RLock()
	defer fs.mutex.RUnlock()

	followUps, err := fs.LoadFollowUps()
	if err != nil {
		return nil, err
	}

	for _, followUp := range followUps {
		if followUp.ID == id {
			return &followUp, nil
		}
	}

	return nil, nil
}

func (fs *followUpStorage) CreateFollowUp(followUp models.FollowUp) error {
	fs.mutex.Lock()
	defer fs.mutex.Unlock()

	if followUp.ID == "" {
		followUp.ID = generateFollowUpUUID()
	}

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

	followUps, err := fs.LoadFollowUps()
	if err != nil {
		return err
	}

	followUps = append(followUps, followUp)

	return fs.SaveFollowUps(followUps)
}

func generateFollowUpUUID() string {
	bytes := make([]byte, 16)
	rand.Read(bytes)
	bytes[6] = (bytes[6] & 0x0f) | 0x40 // Version 4
	bytes[8] = (bytes[8] & 0x3f) | 0x80 // Variant

	return hex.EncodeToString(bytes)
}

func (fs *followUpStorage) UpdateFollowUp(id string, updates models.UpdateFollowUpRequest) error {
	fs.mutex.Lock()
	defer fs.mutex.Unlock()

	followUps, err := fs.LoadFollowUps()
	if err != nil {
		return err
	}

	for i, followUp := range followUps {
		if followUp.ID == id {
			if updates.CustomerName != nil {
				followUps[i].CustomerName = *updates.CustomerName
			}
			if updates.DealStatus != nil {
				followUps[i].DealStatus = *updates.DealStatus
			}
			if updates.CustomerLevel != nil {
				followUps[i].CustomerLevel = *updates.CustomerLevel
			}
			if updates.Industry != nil {
				followUps[i].Industry = *updates.Industry
			}
			if updates.FollowUpTime != nil {
				// Parse the time string
				t, err := time.Parse(time.RFC3339, *updates.FollowUpTime)
				if err == nil {
					followUps[i].FollowUpTime = t
				}
			}
			if updates.NotificationSent != nil {
				followUps[i].NotificationSent = *updates.NotificationSent
			}

			return fs.SaveFollowUps(followUps)
		}
	}

	return nil // FollowUp not found, but not an error
}

func (fs *followUpStorage) DeleteFollowUp(id string) error {
	fs.mutex.Lock()
	defer fs.mutex.Unlock()

	followUps, err := fs.LoadFollowUps()
	if err != nil {
		return err
	}

	for i, followUp := range followUps {
		if followUp.ID == id {
			followUps = append(followUps[:i], followUps[i+1:]...)
			return fs.SaveFollowUps(followUps)
		}
	}

	return nil // FollowUp not found, but not an error
}

func (fs *followUpStorage) SaveFollowUps(followUps []models.FollowUp) error {
	// Ensure the directory exists
	dir := filepath.Dir(fs.filePath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}

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

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

func (fs *followUpStorage) LoadFollowUps() ([]models.FollowUp, error) {
	// Check if file exists
	if _, err := os.Stat(fs.filePath); os.IsNotExist(err) {
		// Return empty slice if file doesn't exist
		return []models.FollowUp{}, nil
	}

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

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

	return followUps, nil
}

func (fs *followUpStorage) GetPendingNotifications() ([]models.FollowUp, error) {
	fs.mutex.RLock()
	defer fs.mutex.RUnlock()

	followUps, err := fs.LoadFollowUps()
	if err != nil {
		return nil, err
	}

	now := time.Now()
	var pending []models.FollowUp

	for _, followUp := range followUps {
		// Check if follow-up time has passed and notification hasn't been sent
		if !followUp.NotificationSent && followUp.FollowUpTime.Before(now) {
			pending = append(pending, followUp)
		}
	}

	return pending, nil
}

func (fs *followUpStorage) MarkNotificationSent(id string) error {
	fs.mutex.Lock()
	defer fs.mutex.Unlock()

	followUps, err := fs.LoadFollowUps()
	if err != nil {
		return err
	}

	for i, followUp := range followUps {
		if followUp.ID == id {
			followUps[i].NotificationSent = true
			return fs.SaveFollowUps(followUps)
		}
	}

	return nil
}
