package usecase

import (
	"context"
	"errors"
	"fmt"
	"log"
	"regexp"
	"strings"
	"time"

	"lune/talentscale/internal/domain"
	"lune/talentscale/internal/pkg/email"
	"lune/talentscale/internal/pkg/email/templates"
	"lune/talentscale/internal/pkg/utils"

	"github.com/google/uuid"
	"golang.org/x/crypto/bcrypt"
)

type testSessionUsecase struct {
	sessionRepo   domain.TestSessionRepository
	candidateRepo domain.CandidateRepository
	userRepo      domain.UserRepository
	roleRepo      domain.RoleRepository
	emailSvc      *email.Service
}

func NewTestSessionUsecase(
	sessionRepo domain.TestSessionRepository,
	candidateRepo domain.CandidateRepository,
	userRepo domain.UserRepository,
	roleRepo domain.RoleRepository,
	emailSvc *email.Service,
) domain.TestSessionUsecase {
	return &testSessionUsecase{
		sessionRepo:   sessionRepo,
		candidateRepo: candidateRepo,
		userRepo:      userRepo,
		roleRepo:      roleRepo,
		emailSvc:      emailSvc,
	}
}

func (u *testSessionUsecase) CreateSession(ctx context.Context, req *domain.TestSessionCreateRequest) (*domain.TestSessionResponse, error) {
	// 1. Validation
	if req.Title == "" {
		return nil, errors.New("title is required")
	}
	if req.Status != string(domain.TestSessionStatusDraft) && req.Status != string(domain.TestSessionStatusScheduled) {
		return nil, errors.New("invalid status: must be draft or scheduled")
	}
	if req.StartsAt != nil && req.EndsAt != nil && req.StartsAt.After(*req.EndsAt) {
		return nil, errors.New("starts_at must be before ends_at")
	}

	// 2. Parse and sanitize emails
	emails := u.sanitizeEmails(req.InvitationEmails)
	if len(emails) == 0 {
		return nil, errors.New("at least one valid invitation email is required")
	}
	if len(emails) > 1000 {
		return nil, errors.New("maximum 1000 emails allowed per session")
	}

	// 3. Get Candidate Role ID
	candRole, err := u.roleRepo.GetByName(ctx, "candidate")
	if err != nil {
		return nil, fmt.Errorf("failed to get candidate role: %w", err)
	}

	// 4. Process Candidates and Session Items
	sessionID := uuid.New()
	var sessionItems []domain.TestSessionItem
	var inviteData []struct {
		Email    string
		Password string
		Name     string
	}

	for _, emailAddr := range emails {
		// Check existing candidate
		candidate, err := u.candidateRepo.GetByEmail(ctx, emailAddr)

		var password string
		var candidateID uuid.UUID

		if err != nil {
			// Candidate not exists, create it
			password = utils.GenerateSecurePassword()
			candidateID = uuid.New()
			userID := uuid.New()

			// 1. Create Candidate FIRST
			candRecord := &domain.Candidate{
				ID:        candidateID,
				Code:      "C-" + time.Now().Format("2006") + "-" + candidateID.String()[:8],
				Name:      emailAddr,
				Email:     emailAddr,
				CompanyID: req.CompanyID,
				Status:    "new",
				CreatedAt: time.Now(),
				UpdatedAt: time.Now(),
			}

			if err := u.candidateRepo.Create(ctx, candRecord); err != nil {
				log.Printf("Failed to create candidate record for %s: %v", emailAddr, err)
				continue
			}

			// 2. Hash password
			hashed, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)

			// 3. Create User
			user := &domain.User{
				ID:          userID,
				Name:        emailAddr,
				Email:       emailAddr,
				Password:    string(hashed),
				RoleID:      candRole.ID,
				CompanyID:   &req.CompanyID,
				CandidateID: &candRecord.ID,
				CreatedAt:   time.Now(),
				UpdatedAt:   time.Now(),
			}

			if err := u.userRepo.Create(ctx, user); err != nil {
				log.Printf("Failed to create user for %s: %v", emailAddr, err)
				continue
			}

			// 4. Update Candidate.user_id (Circular FK Persistence)
			log.Printf("[Debug] Circular Link Start: CandidateID=%s, UserID=%s", candidateID, userID)
			if err := u.candidateRepo.UpdateUserID(ctx, candidateID, userID); err != nil {
				log.Printf("[Debug] Circular Link FAILED: %v", err)
			} else {
				log.Printf("[Debug] Circular Link SUCCESS: candidates.user_id updated")
			}

		} else {
			candidateID = candidate.ID
		}

		// Create Session Item
		item := domain.TestSessionItem{
			ID:            uuid.New(),
			TestSessionID: sessionID,
			CandidateID:   candidateID,
			Email:         emailAddr,
			CreatedAt:     time.Now(),
		}

		if req.Status == string(domain.TestSessionStatusScheduled) {
			now := time.Now()
			item.InvitedAt = &now

			inviteData = append(inviteData, struct {
				Email    string
				Password string
				Name     string
			}{
				Email:    emailAddr,
				Password: password,
				Name:     emailAddr,
			})
		}

		sessionItems = append(sessionItems, item)
	}

	// 5. Create Session Object
	session := &domain.TestSession{
		ID:              sessionID,
		CompanyID:       req.CompanyID,
		CreatedBy:       req.CreatedBy,
		Title:           req.Title,
		Description:     req.Description,
		StartsAt:        req.StartsAt,
		EndsAt:          req.EndsAt,
		Status:          domain.TestSessionStatus(req.Status),
		TotalCandidates: len(sessionItems),
		CreatedAt:       time.Now(),
		UpdatedAt:       time.Now(),
	}

	// 6. Save Session and Items (Repository handles transaction)
	if err := u.sessionRepo.Create(ctx, session, sessionItems); err != nil {
		return nil, err
	}

	// 7. Send Emails (after commit)
	sentCount := 0
	failedCount := 0
	if req.Status == string(domain.TestSessionStatusScheduled) {
		for _, data := range inviteData {
			// In real world, loginURL should point to the frontend
			loginURL := "https://talent.talentscale.id/login"

			// Build email content
			emailContent := fmt.Sprintf(`
	<div style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
		
		<h2 style="color: #2563eb;">
			Assessment Invitation
		</h2>

		<p>
			Hello,
		</p>

		<p>
			You have been invited to participate in a recruitment assessment session on 
			<strong>TalentScale</strong>.
		</p>

		<table style="border-collapse: collapse; width: 100%%; margin-top: 16px;">
			<tr>
				<td style="padding: 8px; border: 1px solid #ddd;">
					<strong>Company</strong>
				</td>
				<td style="padding: 8px; border: 1px solid #ddd;">
					%s
				</td>
			</tr>

			<tr>
				<td style="padding: 8px; border: 1px solid #ddd;">
					<strong>Session</strong>
				</td>
				<td style="padding: 8px; border: 1px solid #ddd;">
					%s
				</td>
			</tr>

			<tr>
				<td style="padding: 8px; border: 1px solid #ddd;">
					<strong>Start Time</strong>
				</td>
				<td style="padding: 8px; border: 1px solid #ddd;">
					%s
				</td>
			</tr>

			<tr>
				<td style="padding: 8px; border: 1px solid #ddd;">
					<strong>End Time</strong>
				</td>
				<td style="padding: 8px; border: 1px solid #ddd;">
					%s
				</td>
			</tr>
		</table>

		<hr style="margin: 24px 0;">

		<h3 style="margin-bottom: 8px;">
			Login Credentials
		</h3>

		<p>
			Please use the following credentials to access your assessment:
		</p>

		<div style="
			background-color: #f5f5f5;
			padding: 16px;
			border-radius: 8px;
			border: 1px solid #ddd;
		">
			<p style="margin: 0 0 8px 0;">
				<strong>Portal URL:</strong><br>
				<a href="%s">%s</a>
			</p>

			<p style="margin: 0 0 8px 0;">
				<strong>Email:</strong><br>
				%s
			</p>

			<p style="margin: 0;">
				<strong>Password:</strong><br>
				%s
			</p>
		</div>

		<p style="margin-top: 24px;">
			Please complete the assessment before the session expires.
		</p>

		<p>
			If you experience any issues, please contact the recruiter.
		</p>

		<br>

		<p>
			Best regards,<br>
			<strong>TalentScale Team</strong>
		</p>

	</div>
`,
				"TalentScale",
				session.Title,
				session.StartsAt.Format("02 Jan 2006 15:04"),
				session.EndsAt.Format("02 Jan 2006 15:04"),
				loginURL,
				loginURL,
				data.Email,
				data.Password,
			)

			body := templates.BaseLayout(
				"Test Session Invitation — TalentScale",
				emailContent,
			)

			// Send using centralized service (has retry and logging)
			u.emailSvc.SendWithRetry(email.Payload{
				To:      []string{data.Email},
				Subject: fmt.Sprintf("Invitation: %s", session.Title),
				Body:    body,
			}, 3)
			sentCount++
		}
	}

	return &domain.TestSessionResponse{
		Session:         session,
		TotalCandidates: len(sessionItems),
		SentEmails:      sentCount,
		FailedEmails:    failedCount,
	}, nil
}

func (u *testSessionUsecase) GetSession(ctx context.Context, id uuid.UUID, companyID uuid.UUID) (*domain.TestSessionResponse, error) {
	session, items, err := u.sessionRepo.GetByID(ctx, id, companyID)
	if err != nil {
		return nil, err
	}
	return &domain.TestSessionResponse{
		Session:         session,
		TotalCandidates: len(items),
	}, nil
}

func (u *testSessionUsecase) ListSessions(ctx context.Context, companyID uuid.UUID, limit, page int) ([]domain.TestSession, int, error) {
	offset := (page - 1) * limit
	return u.sessionRepo.List(ctx, companyID, limit, offset)
}

func (u *testSessionUsecase) sanitizeEmails(raw string) []string {
	if raw == "" {
		return nil
	}

	// Split by ;
	parts := strings.Split(raw, ";")
	uniqueEmails := make(map[string]bool)
	var result []string

	emailRegex := regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`)

	for _, p := range parts {
		email := strings.TrimSpace(strings.ToLower(p))
		if email == "" {
			continue
		}
		if emailRegex.MatchString(email) && !uniqueEmails[email] {
			uniqueEmails[email] = true
			result = append(result, email)
		}
	}
	return result
}
