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.CustomerID == 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.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 } } 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) }