package repository import ( "aggios-app/backend/internal/domain" "database/sql" "fmt" ) type SolutionRepository struct { db *sql.DB } func NewSolutionRepository(db *sql.DB) *SolutionRepository { return &SolutionRepository{db: db} } // ==================== SOLUTIONS ==================== func (r *SolutionRepository) CreateSolution(solution *domain.Solution) error { query := ` INSERT INTO solutions (id, name, slug, icon, description, is_active) VALUES ($1, $2, $3, $4, $5, $6) RETURNING created_at, updated_at ` return r.db.QueryRow( query, solution.ID, solution.Name, solution.Slug, solution.Icon, solution.Description, solution.IsActive, ).Scan(&solution.CreatedAt, &solution.UpdatedAt) } func (r *SolutionRepository) GetAllSolutions() ([]domain.Solution, error) { query := ` SELECT id, name, slug, icon, description, is_active, created_at, updated_at FROM solutions ORDER BY created_at DESC ` rows, err := r.db.Query(query) if err != nil { return nil, err } defer rows.Close() var solutions []domain.Solution for rows.Next() { var s domain.Solution err := rows.Scan( &s.ID, &s.Name, &s.Slug, &s.Icon, &s.Description, &s.IsActive, &s.CreatedAt, &s.UpdatedAt, ) if err != nil { return nil, err } solutions = append(solutions, s) } return solutions, nil } func (r *SolutionRepository) GetActiveSolutions() ([]domain.Solution, error) { query := ` SELECT id, name, slug, icon, description, is_active, created_at, updated_at FROM solutions WHERE is_active = true ORDER BY name ` rows, err := r.db.Query(query) if err != nil { return nil, err } defer rows.Close() var solutions []domain.Solution for rows.Next() { var s domain.Solution err := rows.Scan( &s.ID, &s.Name, &s.Slug, &s.Icon, &s.Description, &s.IsActive, &s.CreatedAt, &s.UpdatedAt, ) if err != nil { return nil, err } solutions = append(solutions, s) } return solutions, nil } func (r *SolutionRepository) GetSolutionByID(id string) (*domain.Solution, error) { query := ` SELECT id, name, slug, icon, description, is_active, created_at, updated_at FROM solutions WHERE id = $1 ` var s domain.Solution err := r.db.QueryRow(query, id).Scan( &s.ID, &s.Name, &s.Slug, &s.Icon, &s.Description, &s.IsActive, &s.CreatedAt, &s.UpdatedAt, ) if err != nil { return nil, err } return &s, nil } func (r *SolutionRepository) GetSolutionBySlug(slug string) (*domain.Solution, error) { query := ` SELECT id, name, slug, icon, description, is_active, created_at, updated_at FROM solutions WHERE slug = $1 ` var s domain.Solution err := r.db.QueryRow(query, slug).Scan( &s.ID, &s.Name, &s.Slug, &s.Icon, &s.Description, &s.IsActive, &s.CreatedAt, &s.UpdatedAt, ) if err != nil { return nil, err } return &s, nil } func (r *SolutionRepository) UpdateSolution(solution *domain.Solution) error { query := ` UPDATE solutions SET name = $1, slug = $2, icon = $3, description = $4, is_active = $5, updated_at = CURRENT_TIMESTAMP WHERE id = $6 ` result, err := r.db.Exec( query, solution.Name, solution.Slug, solution.Icon, solution.Description, solution.IsActive, solution.ID, ) if err != nil { return err } rows, err := result.RowsAffected() if err != nil { return err } if rows == 0 { return fmt.Errorf("solution not found") } return nil } func (r *SolutionRepository) DeleteSolution(id string) error { query := `DELETE FROM solutions WHERE id = $1` result, err := r.db.Exec(query, id) if err != nil { return err } rows, err := result.RowsAffected() if err != nil { return err } if rows == 0 { return fmt.Errorf("solution not found") } return nil } // ==================== PLAN <-> SOLUTION ==================== func (r *SolutionRepository) AddSolutionToPlan(planID, solutionID string) error { query := ` INSERT INTO plan_solutions (plan_id, solution_id) VALUES ($1, $2) ON CONFLICT (plan_id, solution_id) DO NOTHING ` _, err := r.db.Exec(query, planID, solutionID) return err } func (r *SolutionRepository) RemoveSolutionFromPlan(planID, solutionID string) error { query := `DELETE FROM plan_solutions WHERE plan_id = $1 AND solution_id = $2` _, err := r.db.Exec(query, planID, solutionID) return err } func (r *SolutionRepository) GetPlanSolutions(planID string) ([]domain.Solution, error) { query := ` SELECT s.id, s.name, s.slug, s.icon, s.description, s.is_active, s.created_at, s.updated_at FROM solutions s INNER JOIN plan_solutions ps ON s.id = ps.solution_id WHERE ps.plan_id = $1 ORDER BY s.name ` rows, err := r.db.Query(query, planID) if err != nil { return nil, err } defer rows.Close() var solutions []domain.Solution for rows.Next() { var s domain.Solution err := rows.Scan( &s.ID, &s.Name, &s.Slug, &s.Icon, &s.Description, &s.IsActive, &s.CreatedAt, &s.UpdatedAt, ) if err != nil { return nil, err } solutions = append(solutions, s) } return solutions, nil } func (r *SolutionRepository) SetPlanSolutions(planID string, solutionIDs []string) error { // Inicia transação tx, err := r.db.Begin() if err != nil { return err } // Remove todas as soluções antigas do plano _, err = tx.Exec(`DELETE FROM plan_solutions WHERE plan_id = $1`, planID) if err != nil { tx.Rollback() return err } // Adiciona as novas soluções stmt, err := tx.Prepare(`INSERT INTO plan_solutions (plan_id, solution_id) VALUES ($1, $2)`) if err != nil { tx.Rollback() return err } defer stmt.Close() for _, solutionID := range solutionIDs { _, err = stmt.Exec(planID, solutionID) if err != nil { tx.Rollback() return err } } return tx.Commit() } func (r *SolutionRepository) GetTenantSolutions(tenantID string) ([]domain.Solution, error) { query := ` SELECT DISTINCT s.id, s.name, s.slug, s.icon, s.description, s.is_active, s.created_at, s.updated_at FROM solutions s INNER JOIN plan_solutions ps ON s.id = ps.solution_id INNER JOIN agency_subscriptions asub ON ps.plan_id = asub.plan_id WHERE asub.agency_id = $1 AND s.is_active = true AND asub.status = 'active' ORDER BY s.name ` rows, err := r.db.Query(query, tenantID) if err != nil { return nil, err } defer rows.Close() var solutions []domain.Solution for rows.Next() { var s domain.Solution err := rows.Scan( &s.ID, &s.Name, &s.Slug, &s.Icon, &s.Description, &s.IsActive, &s.CreatedAt, &s.UpdatedAt, ) if err != nil { return nil, err } solutions = append(solutions, s) } // Se não encontrou via subscription, retorna array vazio if solutions == nil { solutions = []domain.Solution{} } return solutions, nil }