package service

import (
	"context"
	"fmt"

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

// AddDomain adds a new domain to a project.
// For threesix.ai subdomains, it creates the DNS record automatically.
// For external domains, it returns instructions for the user to configure their DNS.
func (s *ProjectInfraService) AddDomain(ctx context.Context, req AddDomainRequest) (*domain.ProjectDomain, error) {
	if s.domainRepo == nil {
		return nil, fmt.Errorf("domain repository not configured")
	}

	// Validate domain format
	if err := domain.ValidateFQDN(req.Domain); err != nil {
		return nil, fmt.Errorf("invalid domain: %w", err)
	}

	// Check if domain already exists
	exists, err := s.domainRepo.Exists(ctx, req.Domain)
	if err != nil {
		return nil, fmt.Errorf("failed to check domain: %w", err)
	}
	if exists {
		return nil, domain.ErrDuplicateDomain
	}

	// Default record type
	recordType := req.RecordType
	if recordType == "" {
		recordType = "A"
	}

	// Default domain type
	domainType := req.Type
	if domainType == "" {
		domainType = domain.DomainTypeAlias
	}

	pd := &domain.ProjectDomain{
		ProjectID:     req.ProjectID,
		Domain:        req.Domain,
		Type:          domainType,
		DNSRecordType: recordType,
		Verified:      false,
	}

	// Create DNS record for threesix.ai subdomains
	if domain.IsSubdomainOf(req.Domain, s.defaultDomain) && s.dns != nil {
		subdomain := domain.ExtractSubdomain(req.Domain, s.defaultDomain)
		content := s.clusterIP
		if recordType == "CNAME" {
			// CNAME points to the project's primary auto domain
			status, err := s.GetStatus(ctx, req.ProjectID)
			if err == nil && status.Slug != "" {
				content = status.Slug + "." + s.defaultDomain
			}
		}

		dnsRecord, err := s.dns.UpsertRecord(ctx, domain.DNSRecord{
			Type:    recordType,
			Name:    subdomain,
			Content: content,
			TTL:     1,
			Proxied: req.Proxied,
		})
		if err != nil {
			return nil, fmt.Errorf("failed to upsert DNS record: %w", err)
		}
		pd.DNSRecordID = dnsRecord.ID
		pd.Verified = true
	}

	// Store in database
	if err := s.domainRepo.Create(ctx, pd); err != nil {
		// Rollback DNS record if database insert fails
		if pd.DNSRecordID != "" && s.dns != nil {
			_ = s.dns.DeleteRecord(ctx, pd.DNSRecordID)
		}
		return nil, fmt.Errorf("failed to store domain: %w", err)
	}

	// Add host to K8s ingress (for both threesix.ai and external domains)
	log := logging.FromContext(ctx).WithService("project_infra")
	if s.deployer != nil {
		if err := s.deployer.AddIngressHost(ctx, req.ProjectID, req.Domain); err != nil {
			log.Warn("failed to add ingress host", logging.FieldError, err, logging.FieldProjectID, req.ProjectID, "domain", req.Domain)
			// Don't fail the request - DNS and DB are already set up
		}
	}

	log.Info("domain added", logging.FieldProjectID, req.ProjectID, "domain", req.Domain, "type", domainType)
	return pd, nil
}

// ListDomains returns all domains for a project.
func (s *ProjectInfraService) ListDomains(ctx context.Context, projectID string) ([]*domain.ProjectDomain, error) {
	if s.domainRepo == nil {
		return nil, fmt.Errorf("domain repository not configured")
	}
	return s.domainRepo.ListByProject(ctx, projectID)
}

// RemoveDomain removes a domain from a project.
// Returns error if attempting to remove the primary_auto domain.
func (s *ProjectInfraService) RemoveDomain(ctx context.Context, projectID, fqdn string) error {
	if s.domainRepo == nil {
		return fmt.Errorf("domain repository not configured")
	}

	// Get the domain record
	pd, err := s.domainRepo.GetByDomain(ctx, fqdn)
	if err != nil {
		return fmt.Errorf("failed to get domain: %w", err)
	}
	if pd == nil {
		return domain.ErrDomainNotFound
	}

	// Verify it belongs to this project
	if pd.ProjectID != projectID {
		return fmt.Errorf("domain does not belong to project")
	}

	// Prevent deletion of primary_auto domain
	if pd.Type == domain.DomainTypePrimaryAuto {
		return fmt.Errorf("cannot remove auto-generated primary domain; delete the project instead")
	}

	log := logging.FromContext(ctx).WithService("project_infra")

	// Delete DNS record if we have the record ID
	if pd.DNSRecordID != "" && s.dns != nil {
		if err := s.dns.DeleteRecord(ctx, pd.DNSRecordID); err != nil {
			log.Warn("failed to delete DNS record", logging.FieldError, err, "domain", fqdn)
		}
	} else if domain.IsSubdomainOf(fqdn, s.defaultDomain) && s.dns != nil {
		// Fallback: try to delete by name
		subdomain := domain.ExtractSubdomain(fqdn, s.defaultDomain)
		if subdomain != "" {
			_ = s.dns.DeleteRecordByName(ctx, pd.DNSRecordType, subdomain)
		}
	}

	// Remove host from K8s ingress
	if s.deployer != nil {
		if err := s.deployer.RemoveIngressHost(ctx, projectID, fqdn); err != nil {
			log.Warn("failed to remove ingress host", logging.FieldError, err, logging.FieldProjectID, projectID, "domain", fqdn)
			// Continue anyway - DNS record is already deleted
		}
	}

	// Delete from database
	if err := s.domainRepo.Delete(ctx, pd.ID); err != nil {
		return fmt.Errorf("failed to delete domain: %w", err)
	}

	log.Info("domain removed", logging.FieldProjectID, projectID, "domain", fqdn)
	return nil
}

// GetPrimaryDomain returns the primary domain for a project.
func (s *ProjectInfraService) GetPrimaryDomain(ctx context.Context, projectID string) (*domain.ProjectDomain, error) {
	if s.domainRepo == nil {
		return nil, fmt.Errorf("domain repository not configured")
	}
	return s.domainRepo.GetPrimary(ctx, projectID)
}
