package handler

import (
	"encoding/json"
	"io"
	"net/http"
	"regexp"

	"freeleaps.com/gitea-webhook-ambassador/internal/config"
	"freeleaps.com/gitea-webhook-ambassador/internal/database"
	"freeleaps.com/gitea-webhook-ambassador/internal/logger"
	"freeleaps.com/gitea-webhook-ambassador/internal/model"
	"freeleaps.com/gitea-webhook-ambassador/internal/worker"
)

// WebhookHandler handles incoming Gitea webhooks
type WebhookHandler struct {
	workerPool *worker.Pool
	db         *database.DB
	config     *config.Configuration
}

// NewWebhookHandler creates a new webhook handler
func NewWebhookHandler(workerPool *worker.Pool, db *database.DB, config *config.Configuration) *WebhookHandler {
	return &WebhookHandler{
		workerPool: workerPool,
		db:         db,
		config:     config,
	}
}

// HandleWebhook processes incoming webhook requests
func (h *WebhookHandler) HandleWebhook(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	// Verify signature if secret token is set
	secretHeader := h.config.Server.SecretHeader
	serverSecretKey := h.config.Server.SecretKey

	receivedSecretKey := r.Header.Get(secretHeader)
	if receivedSecretKey == "" {
		http.Error(w, "No secret key provided", http.StatusUnauthorized)
		logger.Warn("No secret key provided in header")
		return
	}
	if receivedSecretKey != serverSecretKey {
		http.Error(w, "Invalid secret key", http.StatusUnauthorized)
		logger.Warn("Invalid secret key provided")
		return
	}

	// Read and parse the webhook payload
	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Failed to read request body", http.StatusInternalServerError)
		logger.Error("Failed to read webhook body: %v", err)
		return
	}
	defer r.Body.Close()

	var webhook model.GiteaWebhook
	if err := json.Unmarshal(body, &webhook); err != nil {
		http.Error(w, "Failed to parse webhook payload", http.StatusBadRequest)
		logger.Error("Failed to parse webhook payload: %v", err)
		return
	}

	// Get project mapping from database
	project, err := h.db.GetProjectMapping(webhook.Repository.FullName)
	if err != nil {
		logger.Error("Failed to get project mapping: %v", err)
		http.Error(w, "Internal server error", http.StatusInternalServerError)
		return
	}
	if project == nil {
		logger.Info("No Jenkins job mapping for repository: %s", webhook.Repository.FullName)
		w.WriteHeader(http.StatusOK) // Still return OK to not alarm Gitea
		return
	}

	// Extract branch name from ref
	branchName := webhook.GetBranchName()

	// Determine which job to trigger based on branch name
	jobName := h.determineJobName(project, branchName)
	if jobName == "" {
		logger.Info("No job configured to trigger for repository %s, branch %s",
			webhook.Repository.FullName, branchName)
		w.WriteHeader(http.StatusOK)
		return
	}

	// Prepare parameters for Jenkins job
	params := map[string]string{
		"BRANCH_NAME":     branchName,
		"COMMIT_SHA":      webhook.After,
		"REPOSITORY_URL":  webhook.Repository.CloneURL,
		"REPOSITORY_NAME": webhook.Repository.FullName,
		"PUSHER_NAME":     webhook.Pusher.Login,
		"PUSHER_EMAIL":    webhook.Pusher.Email,
	}

	// Submit the job to the worker pool
	job := worker.Job{
		Name:           jobName,
		Parameters:     params,
		EventID:        webhook.GetEventID(),
		RepositoryName: webhook.Repository.FullName,
		BranchName:     branchName,
		CommitSHA:      webhook.After,
		Attempts:       0,
	}

	if h.workerPool.Submit(job) {
		logger.Info("Webhook received and queued for repository %s, branch %s, commit %s, job %s",
			webhook.Repository.FullName, branchName, webhook.After, jobName)
		w.WriteHeader(http.StatusAccepted)
	} else {
		logger.Warn("Failed to queue webhook: queue full")
		http.Error(w, "Server busy, try again later", http.StatusServiceUnavailable)
	}
}

// determineJobName selects the appropriate Jenkins job to trigger based on branch name
func (h *WebhookHandler) determineJobName(project *database.ProjectMapping, branchName string) string {
	// First check for exact branch match
	for _, job := range project.BranchJobs {
		if job.BranchName == branchName {
			logger.Debug("Found exact branch match for %s: job %s", branchName, job.JobName)
			return job.JobName
		}
	}

	// Then check for pattern-based matches
	for _, pattern := range project.BranchPatterns {
		matched, err := regexp.MatchString(pattern.Pattern, branchName)
		if err != nil {
			logger.Error("Error matching branch pattern %s: %v", pattern.Pattern, err)
			continue
		}
		if matched {
			logger.Debug("Branch %s matched pattern %s: job %s", branchName, pattern.Pattern, pattern.JobName)
			return pattern.JobName
		}
	}

	// Fall back to default job if available
	if project.DefaultJob != "" {
		logger.Debug("Using default job for branch %s: job %s", branchName, project.DefaultJob)
		return project.DefaultJob
	}

	return ""
}
