// Package service provides business logic services.
package service

import (
	"context"
	"database/sql"
	"fmt"
	"net/http"
	"time"

	"github.com/orchard9/rdev/internal/domain"
	"github.com/orchard9/rdev/internal/port"
)

// ValidateProjectName validates that a project name is safe for use as
// a DNS subdomain, K8s resource name, and git repository name.
// Delegates to domain.ValidateProjectName for centralized validation.
func ValidateProjectName(name string) error {
	return domain.ValidateProjectName(name)
}

// ProjectInfraService orchestrates project infrastructure operations.
// It coordinates git repo creation, DNS, CI activation, template seeding, deployment,
// and database/cache provisioning.
type ProjectInfraService struct {
	db                 *sql.DB
	gitRepo            port.GitRepository
	dns                port.DNSProvider
	deployer           port.Deployer
	ciProvider         port.CIProvider
	templateProvider   port.TemplateProvider
	domainRepo         port.ProjectDomainRepository
	slugGenerator      port.SlugGenerator
	credentialStore    port.CredentialStore
	dbProvisioner      port.DatabaseProvisioner
	cacheProvisioner   port.CacheProvisioner
	storageProvisioner port.StorageProvisioner
	registryProvider   port.RegistryProvider

	// Config
	defaultGitOwner string
	defaultDomain   string
	clusterIP       string
	registryURL     string // e.g., "registry.threesix.ai"
}

// ProjectInfraConfig configures the project infrastructure service.
type ProjectInfraConfig struct {
	DefaultGitOwner string // e.g., "threesix"
	DefaultDomain   string // e.g., "threesix.ai"
	ClusterIP       string // e.g., "208.122.204.172"
	RegistryURL     string // e.g., "registry.threesix.ai"
}

// NewProjectInfraService creates a new project infrastructure service.
func NewProjectInfraService(
	db *sql.DB,
	gitRepo port.GitRepository,
	dns port.DNSProvider,
	deployer port.Deployer,
	ciProvider port.CIProvider,
	templateProvider port.TemplateProvider,
	domainRepo port.ProjectDomainRepository,
	slugGenerator port.SlugGenerator,
	cfg ProjectInfraConfig,
) *ProjectInfraService {
	registryURL := cfg.RegistryURL
	if registryURL == "" {
		registryURL = "registry.threesix.ai" // Default for backward compatibility
	}
	return &ProjectInfraService{
		db:               db,
		gitRepo:          gitRepo,
		dns:              dns,
		deployer:         deployer,
		ciProvider:       ciProvider,
		templateProvider: templateProvider,
		domainRepo:       domainRepo,
		slugGenerator:    slugGenerator,
		defaultGitOwner:  cfg.DefaultGitOwner,
		defaultDomain:    cfg.DefaultDomain,
		clusterIP:        cfg.ClusterIP,
		registryURL:      registryURL,
	}
}

// WithCredentialStore sets the credential store for storing provisioned credentials.
func (s *ProjectInfraService) WithCredentialStore(cs port.CredentialStore) *ProjectInfraService {
	s.credentialStore = cs
	return s
}

// WithDatabaseProvisioner sets the database provisioner for project databases.
func (s *ProjectInfraService) WithDatabaseProvisioner(dp port.DatabaseProvisioner) *ProjectInfraService {
	s.dbProvisioner = dp
	return s
}

// WithCacheProvisioner sets the cache provisioner for project cache access.
func (s *ProjectInfraService) WithCacheProvisioner(cp port.CacheProvisioner) *ProjectInfraService {
	s.cacheProvisioner = cp
	return s
}

// WithStorageProvisioner sets the storage provisioner for project storage (GCS).
func (s *ProjectInfraService) WithStorageProvisioner(sp port.StorageProvisioner) *ProjectInfraService {
	s.storageProvisioner = sp
	return s
}

// WithRegistryProvider sets the container registry provider for image cleanup.
func (s *ProjectInfraService) WithRegistryProvider(rp port.RegistryProvider) *ProjectInfraService {
	s.registryProvider = rp
	return s
}

// CreateProjectRequest contains parameters for creating a new project.
type CreateProjectRequest struct {
	Name            string
	Description     string
	Private         bool
	Template        string // Template to seed the repo with (default: "skeleton" for composable monorepos)
	CustomSubdomain string // Optional: custom subdomain (e.g., "my-app" for my-app.threesix.ai)
}

// CreateProjectResult contains the result of project creation.
type CreateProjectResult struct {
	ProjectID   string
	Name        string
	Description string
	Slug        string // Auto-generated unique identifier

	// Git info
	GitRepoOwner string
	GitRepoName  string
	CloneSSH     string
	CloneHTTP    string
	HTMLURL      string

	// Domain info (primary domain for backward compatibility)
	Domain string
	URL    string

	// API documentation URL (docs.{slug}.{domain})
	DocsURL string

	// All domains associated with the project
	Domains []*domain.ProjectDomain

	// Next steps
	NextSteps []string
}

// ProjectStatus represents the current status of a project.
type ProjectStatus struct {
	ProjectID   string
	Name        string
	Description string
	Slug        string

	// Git
	GitRepoOwner string
	GitRepoName  string
	CloneSSH     string
	CloneHTTP    string
	HTMLURL      string

	// Domain (primary for backward compatibility)
	Domain       string
	CustomDomain string
	URL          string

	// All domains associated with the project
	Domains []*domain.ProjectDomain

	// Deployment
	DeploymentImage    string
	DeploymentStatus   string
	DeploymentReplicas int
	ReadyReplicas      int

	// Site health
	SiteLive  bool   // True if the site responds with HTTP 200
	SiteError string // Error message if site check failed
}

// AddDomainRequest contains parameters for adding a domain to a project.
type AddDomainRequest struct {
	ProjectID  string
	Domain     string            // Full domain (e.g., "my-app.threesix.ai" or "custom.example.com")
	Type       domain.DomainType // DomainTypePrimaryCustom or DomainTypeAlias
	RecordType string            // "A" or "CNAME" (default: "A")
	Proxied    bool              // Cloudflare proxy enabled
}

// checkSiteHealth performs an HTTP GET request to check if a site is live.
// Returns (true, "") if the site returns HTTP 200, otherwise (false, errorMessage).
func checkSiteHealth(ctx context.Context, url string) (bool, string) {
	client := &http.Client{
		Timeout: 5 * time.Second,
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse
		},
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
	if err != nil {
		return false, fmt.Sprintf("failed to create request: %v", err)
	}

	resp, err := client.Do(req)
	if err != nil {
		return false, fmt.Sprintf("request failed: %v", err)
	}
	defer func() { _ = resp.Body.Close() }()

	// Accept 2xx and 3xx status codes as "live"
	if resp.StatusCode >= 200 && resp.StatusCode < 400 {
		return true, ""
	}

	return false, fmt.Sprintf("HTTP %d", resp.StatusCode)
}
