package storage

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

type CustomerStorage interface {
	GetAllCustomers() ([]models.Customer, error)
	GetCustomerByID(id string) (*models.Customer, error)
	CreateCustomer(customer models.Customer) error
	UpdateCustomer(id string, updates models.UpdateCustomerRequest) error
	DeleteCustomer(id string) error
	SaveCustomers(customers []models.Customer) error
	LoadCustomers() ([]models.Customer, error)
	CustomerExists(customer models.Customer) (bool, error)
}

type customerStorage struct {
	filePath string
	mutex    sync.RWMutex
}

func NewCustomerStorage(filePath string) CustomerStorage {
	storage := &customerStorage{
		filePath: filePath,
	}

	// Ensure the directory exists
	dir := os.DirFS(filePath[:len(filePath)-len("/customers.json")])
	_ = dir // Use the directory to ensure it exists

	return storage
}

func (cs *customerStorage) GetAllCustomers() ([]models.Customer, error) {
	cs.mutex.RLock()
	defer cs.mutex.RUnlock()

	customers, err := cs.LoadCustomers()
	if err != nil {
		return nil, err
	}

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

	return customers, nil
}

func (cs *customerStorage) GetCustomerByID(id string) (*models.Customer, error) {
	cs.mutex.RLock()
	defer cs.mutex.RUnlock()

	customers, err := cs.LoadCustomers()
	if err != nil {
		return nil, err
	}

	for _, customer := range customers {
		if customer.ID == id {
			return &customer, nil
		}
	}

	return nil, nil
}

func (cs *customerStorage) CreateCustomer(customer models.Customer) error {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()

	if customer.ID == "" {
		customer.ID = generateUUID()
	}

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

	customers, err := cs.LoadCustomers()
	if err != nil {
		return err
	}

	customers = append(customers, customer)

	return cs.SaveCustomers(customers)
}

func generateUUID() 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 (cs *customerStorage) UpdateCustomer(id string, updates models.UpdateCustomerRequest) error {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()

	customers, err := cs.LoadCustomers()
	if err != nil {
		return err
	}

	for i, customer := range customers {
		if customer.ID == id {
			if updates.CustomerName != nil {
				customers[i].CustomerName = *updates.CustomerName
			}
			if updates.IntendedProduct != nil {
				customers[i].IntendedProduct = *updates.IntendedProduct
			}
			if updates.Version != nil {
				customers[i].Version = *updates.Version
			}
			if updates.Description != nil {
				customers[i].Description = *updates.Description
			}
			if updates.Solution != nil {
				customers[i].Solution = *updates.Solution
			}
			if updates.Type != nil {
				customers[i].Type = *updates.Type
			}
			if updates.Module != nil {
				customers[i].Module = *updates.Module
			}
			if updates.StatusProgress != nil {
				customers[i].StatusProgress = *updates.StatusProgress
			}
			if updates.Reporter != nil {
				customers[i].Reporter = *updates.Reporter
			}

			return cs.SaveCustomers(customers)
		}
	}

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

func (cs *customerStorage) DeleteCustomer(id string) error {
	cs.mutex.Lock()
	defer cs.mutex.Unlock()

	customers, err := cs.LoadCustomers()
	if err != nil {
		return err
	}

	for i, customer := range customers {
		if customer.ID == id {
			customers = append(customers[:i], customers[i+1:]...)
			return cs.SaveCustomers(customers)
		}
	}

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

func (cs *customerStorage) SaveCustomers(customers []models.Customer) error {
	// Ensure the directory exists
	dir := filepath.Dir(cs.filePath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return err
	}

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

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

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

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

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

	return customers, nil
}

func (cs *customerStorage) CustomerExists(customer models.Customer) (bool, error) {
	cs.mutex.RLock()
	defer cs.mutex.RUnlock()

	customers, err := cs.LoadCustomers()
	if err != nil {
		return false, err
	}

	for _, existingCustomer := range customers {
		if existingCustomer.Description == customer.Description {
			return true, nil
		}
	}

	return false, nil
}
