package portfolio

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"
)

// Store handles portfolio data persistence
type Store struct {
	pool *pgxpool.Pool
}

// NewStore creates a new portfolio store
func NewStore(pool *pgxpool.Pool) *Store {
	return &Store{pool: pool}
}

// ============================================================================
// Portfolio CRUD Operations
// ============================================================================

// CreatePortfolio creates a new portfolio
func (s *Store) CreatePortfolio(ctx context.Context, p *Portfolio) error {
	p.ID = uuid.New()
	p.CreatedAt = time.Now().UTC()
	p.UpdatedAt = p.CreatedAt
	if p.Status == "" {
		p.Status = PortfolioStatusDraft
	}

	settings, _ := json.Marshal(p.Settings)

	_, err := s.pool.Exec(ctx, `
		INSERT INTO portfolios (
			id, tenant_id, namespace_id,
			name, description, status,
			department, business_unit, owner, owner_email,
			total_assessments, total_roadmaps, total_workshops,
			avg_risk_score, high_risk_count, conditional_count, approved_count,
			compliance_score, settings,
			created_at, updated_at, created_by, approved_at, approved_by
		) VALUES (
			$1, $2, $3,
			$4, $5, $6,
			$7, $8, $9, $10,
			$11, $12, $13,
			$14, $15, $16, $17,
			$18, $19,
			$20, $21, $22, $23, $24
		)
	`,
		p.ID, p.TenantID, p.NamespaceID,
		p.Name, p.Description, string(p.Status),
		p.Department, p.BusinessUnit, p.Owner, p.OwnerEmail,
		p.TotalAssessments, p.TotalRoadmaps, p.TotalWorkshops,
		p.AvgRiskScore, p.HighRiskCount, p.ConditionalCount, p.ApprovedCount,
		p.ComplianceScore, settings,
		p.CreatedAt, p.UpdatedAt, p.CreatedBy, p.ApprovedAt, p.ApprovedBy,
	)

	return err
}

// GetPortfolio retrieves a portfolio by ID
func (s *Store) GetPortfolio(ctx context.Context, id uuid.UUID) (*Portfolio, error) {
	var p Portfolio
	var status string
	var settings []byte

	err := s.pool.QueryRow(ctx, `
		SELECT
			id, tenant_id, namespace_id,
			name, description, status,
			department, business_unit, owner, owner_email,
			total_assessments, total_roadmaps, total_workshops,
			avg_risk_score, high_risk_count, conditional_count, approved_count,
			compliance_score, settings,
			created_at, updated_at, created_by, approved_at, approved_by
		FROM portfolios WHERE id = $1
	`, id).Scan(
		&p.ID, &p.TenantID, &p.NamespaceID,
		&p.Name, &p.Description, &status,
		&p.Department, &p.BusinessUnit, &p.Owner, &p.OwnerEmail,
		&p.TotalAssessments, &p.TotalRoadmaps, &p.TotalWorkshops,
		&p.AvgRiskScore, &p.HighRiskCount, &p.ConditionalCount, &p.ApprovedCount,
		&p.ComplianceScore, &settings,
		&p.CreatedAt, &p.UpdatedAt, &p.CreatedBy, &p.ApprovedAt, &p.ApprovedBy,
	)

	if err == pgx.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	p.Status = PortfolioStatus(status)
	json.Unmarshal(settings, &p.Settings)

	return &p, nil
}

// ListPortfolios lists portfolios for a tenant with optional filters
func (s *Store) ListPortfolios(ctx context.Context, tenantID uuid.UUID, filters *PortfolioFilters) ([]Portfolio, error) {
	query := `
		SELECT
			id, tenant_id, namespace_id,
			name, description, status,
			department, business_unit, owner, owner_email,
			total_assessments, total_roadmaps, total_workshops,
			avg_risk_score, high_risk_count, conditional_count, approved_count,
			compliance_score, settings,
			created_at, updated_at, created_by, approved_at, approved_by
		FROM portfolios WHERE tenant_id = $1`

	args := []interface{}{tenantID}
	argIdx := 2

	if filters != nil {
		if filters.Status != "" {
			query += fmt.Sprintf(" AND status = $%d", argIdx)
			args = append(args, string(filters.Status))
			argIdx++
		}
		if filters.Department != "" {
			query += fmt.Sprintf(" AND department = $%d", argIdx)
			args = append(args, filters.Department)
			argIdx++
		}
		if filters.BusinessUnit != "" {
			query += fmt.Sprintf(" AND business_unit = $%d", argIdx)
			args = append(args, filters.BusinessUnit)
			argIdx++
		}
		if filters.Owner != "" {
			query += fmt.Sprintf(" AND owner ILIKE $%d", argIdx)
			args = append(args, "%"+filters.Owner+"%")
			argIdx++
		}
		if filters.MinRiskScore != nil {
			query += fmt.Sprintf(" AND avg_risk_score >= $%d", argIdx)
			args = append(args, *filters.MinRiskScore)
			argIdx++
		}
		if filters.MaxRiskScore != nil {
			query += fmt.Sprintf(" AND avg_risk_score <= $%d", argIdx)
			args = append(args, *filters.MaxRiskScore)
			argIdx++
		}
	}

	query += " ORDER BY updated_at DESC"

	if filters != nil && filters.Limit > 0 {
		query += fmt.Sprintf(" LIMIT $%d", argIdx)
		args = append(args, filters.Limit)
		argIdx++

		if filters.Offset > 0 {
			query += fmt.Sprintf(" OFFSET $%d", argIdx)
			args = append(args, filters.Offset)
		}
	}

	rows, err := s.pool.Query(ctx, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var portfolios []Portfolio
	for rows.Next() {
		var p Portfolio
		var status string
		var settings []byte

		err := rows.Scan(
			&p.ID, &p.TenantID, &p.NamespaceID,
			&p.Name, &p.Description, &status,
			&p.Department, &p.BusinessUnit, &p.Owner, &p.OwnerEmail,
			&p.TotalAssessments, &p.TotalRoadmaps, &p.TotalWorkshops,
			&p.AvgRiskScore, &p.HighRiskCount, &p.ConditionalCount, &p.ApprovedCount,
			&p.ComplianceScore, &settings,
			&p.CreatedAt, &p.UpdatedAt, &p.CreatedBy, &p.ApprovedAt, &p.ApprovedBy,
		)
		if err != nil {
			return nil, err
		}

		p.Status = PortfolioStatus(status)
		json.Unmarshal(settings, &p.Settings)

		portfolios = append(portfolios, p)
	}

	return portfolios, nil
}

// UpdatePortfolio updates a portfolio
func (s *Store) UpdatePortfolio(ctx context.Context, p *Portfolio) error {
	p.UpdatedAt = time.Now().UTC()

	settings, _ := json.Marshal(p.Settings)

	_, err := s.pool.Exec(ctx, `
		UPDATE portfolios SET
			name = $2, description = $3, status = $4,
			department = $5, business_unit = $6, owner = $7, owner_email = $8,
			settings = $9,
			updated_at = $10, approved_at = $11, approved_by = $12
		WHERE id = $1
	`,
		p.ID, p.Name, p.Description, string(p.Status),
		p.Department, p.BusinessUnit, p.Owner, p.OwnerEmail,
		settings,
		p.UpdatedAt, p.ApprovedAt, p.ApprovedBy,
	)

	return err
}

// DeletePortfolio deletes a portfolio and its items
func (s *Store) DeletePortfolio(ctx context.Context, id uuid.UUID) error {
	// Delete items first
	_, err := s.pool.Exec(ctx, "DELETE FROM portfolio_items WHERE portfolio_id = $1", id)
	if err != nil {
		return err
	}
	// Delete portfolio
	_, err = s.pool.Exec(ctx, "DELETE FROM portfolios WHERE id = $1", id)
	return err
}
