package services import ( "context" "database/sql" "fmt" "strings" "time" "github.com/rede5/gohorsejobs/backend/internal/models" ) type AdminService struct { DB *sql.DB } func NewAdminService(db *sql.DB) *AdminService { return &AdminService{DB: db} } func (s *AdminService) ListCompanies(ctx context.Context, verified *bool) ([]models.Company, error) { baseQuery := ` SELECT id, name, slug, type, document, address, region_id, city_id, phone, email, website, logo_url, description, active, verified, created_at, updated_at FROM companies ` var args []interface{} if verified != nil { baseQuery += " WHERE verified = $1" args = append(args, *verified) } baseQuery += " ORDER BY created_at DESC" rows, err := s.DB.QueryContext(ctx, baseQuery, args...) if err != nil { return nil, err } defer rows.Close() var companies []models.Company for rows.Next() { var c models.Company if err := rows.Scan( &c.ID, &c.Name, &c.Slug, &c.Type, &c.Document, &c.Address, &c.RegionID, &c.CityID, &c.Phone, &c.Email, &c.Website, &c.LogoURL, &c.Description, &c.Active, &c.Verified, &c.CreatedAt, &c.UpdatedAt, ); err != nil { return nil, err } companies = append(companies, c) } return companies, nil } func (s *AdminService) UpdateCompanyStatus(ctx context.Context, id int, active *bool, verified *bool) (*models.Company, error) { company, err := s.getCompanyByID(ctx, id) if err != nil { return nil, err } if active != nil { company.Active = *active } if verified != nil { company.Verified = *verified } company.UpdatedAt = time.Now() query := ` UPDATE companies SET active = $1, verified = $2, updated_at = $3 WHERE id = $4 ` _, err = s.DB.ExecContext(ctx, query, company.Active, company.Verified, company.UpdatedAt, id) if err != nil { return nil, err } return company, nil } func (s *AdminService) DuplicateJob(ctx context.Context, id int) (*models.Job, error) { query := ` SELECT company_id, created_by, title, description, salary_min, salary_max, salary_type, employment_type, work_mode, working_hours, location, region_id, city_id, requirements, benefits, visa_support, language_level FROM jobs WHERE id = $1 ` var job models.Job if err := s.DB.QueryRowContext(ctx, query, id).Scan( &job.CompanyID, &job.CreatedBy, &job.Title, &job.Description, &job.SalaryMin, &job.SalaryMax, &job.SalaryType, &job.EmploymentType, &job.WorkMode, &job.WorkingHours, &job.Location, &job.RegionID, &job.CityID, &job.Requirements, &job.Benefits, &job.VisaSupport, &job.LanguageLevel, ); err != nil { return nil, err } job.Status = "draft" job.IsFeatured = false job.CreatedAt = time.Now() job.UpdatedAt = time.Now() insertQuery := ` INSERT INTO jobs ( company_id, created_by, title, description, salary_min, salary_max, salary_type, employment_type, work_mode, working_hours, location, region_id, city_id, requirements, benefits, visa_support, language_level, status, is_featured, created_at, updated_at ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21) RETURNING id ` if err := s.DB.QueryRowContext(ctx, insertQuery, job.CompanyID, job.CreatedBy, job.Title, job.Description, job.SalaryMin, job.SalaryMax, job.SalaryType, job.EmploymentType, job.WorkMode, job.WorkingHours, job.Location, job.RegionID, job.CityID, job.Requirements, job.Benefits, job.VisaSupport, job.LanguageLevel, job.Status, job.IsFeatured, job.CreatedAt, job.UpdatedAt, ).Scan(&job.ID); err != nil { return nil, err } return &job, nil } func (s *AdminService) ListTags(ctx context.Context, category *string) ([]models.Tag, error) { baseQuery := `SELECT id, name, category, active, created_at, updated_at FROM job_tags` var args []interface{} if category != nil && *category != "" { baseQuery += " WHERE category = $1" args = append(args, *category) } baseQuery += " ORDER BY name ASC" rows, err := s.DB.QueryContext(ctx, baseQuery, args...) if err != nil { return nil, err } defer rows.Close() var tags []models.Tag for rows.Next() { var t models.Tag if err := rows.Scan(&t.ID, &t.Name, &t.Category, &t.Active, &t.CreatedAt, &t.UpdatedAt); err != nil { return nil, err } tags = append(tags, t) } return tags, nil } func (s *AdminService) CreateTag(ctx context.Context, name string, category string) (*models.Tag, error) { if strings.TrimSpace(name) == "" { return nil, fmt.Errorf("tag name is required") } now := time.Now() tag := models.Tag{ Name: strings.TrimSpace(name), Category: category, Active: true, CreatedAt: now, UpdatedAt: now, } query := ` INSERT INTO job_tags (name, category, active, created_at, updated_at) VALUES ($1, $2, $3, $4, $5) RETURNING id ` if err := s.DB.QueryRowContext(ctx, query, tag.Name, tag.Category, tag.Active, tag.CreatedAt, tag.UpdatedAt).Scan(&tag.ID); err != nil { return nil, err } return &tag, nil } func (s *AdminService) UpdateTag(ctx context.Context, id int, name *string, active *bool) (*models.Tag, error) { tag, err := s.getTagByID(ctx, id) if err != nil { return nil, err } if name != nil { trimmed := strings.TrimSpace(*name) if trimmed != "" { tag.Name = trimmed } } if active != nil { tag.Active = *active } tagUpdatedAt := time.Now() query := ` UPDATE job_tags SET name = $1, active = $2, updated_at = $3 WHERE id = $4 ` _, err = s.DB.ExecContext(ctx, query, tag.Name, tag.Active, tagUpdatedAt, id) if err != nil { return nil, err } tag.UpdatedAt = tagUpdatedAt return tag, nil } func (s *AdminService) getCompanyByID(ctx context.Context, id int) (*models.Company, error) { query := ` SELECT id, name, slug, type, document, address, region_id, city_id, phone, email, website, logo_url, description, active, verified, created_at, updated_at FROM companies WHERE id = $1 ` var c models.Company if err := s.DB.QueryRowContext(ctx, query, id).Scan( &c.ID, &c.Name, &c.Slug, &c.Type, &c.Document, &c.Address, &c.RegionID, &c.CityID, &c.Phone, &c.Email, &c.Website, &c.LogoURL, &c.Description, &c.Active, &c.Verified, &c.CreatedAt, &c.UpdatedAt, ); err != nil { return nil, err } return &c, nil } func (s *AdminService) getTagByID(ctx context.Context, id int) (*models.Tag, error) { query := `SELECT id, name, category, active, created_at, updated_at FROM job_tags WHERE id = $1` var t models.Tag if err := s.DB.QueryRowContext(ctx, query, id).Scan(&t.ID, &t.Name, &t.Category, &t.Active, &t.CreatedAt, &t.UpdatedAt); err != nil { return nil, err } return &t, nil }