package gitops

import (
	"encoding/json"
	"log"
	"time"

	"github.com/forgeo/forgebucket/internal/events"
	"github.com/forgeo/forgebucket/internal/models"
)

// TriggerSync creates a Deployment record in "pending" state and fires
// deployment.started — the same path as a manually-triggered deployment.
// GitOps is just the trigger; actual execution is handled externally (or via CI).
func (c *Controller) TriggerSync(cfg models.GitOpsConfig, desiredSHA string) {
	var env models.Environment
	if found, _ := c.db.ID(cfg.EnvID).Get(&env); !found {
		log.Printf("gitops: sync env %d not found", cfg.EnvID)
		return
	}

	now := time.Now().UTC()
	deploy := &models.Deployment{
		EnvID:       cfg.EnvID,
		RepoID:      cfg.RepoID,
		SHA:         desiredSHA,
		Ref:         "refs/heads/" + cfg.Branch,
		Status:      models.DeployStatusPending,
		TriggeredBy: "gitops",
		Description: "GitOps auto-sync",
		StartedAt:   &now,
	}
	if _, err := c.db.Insert(deploy); err != nil {
		log.Printf("gitops: create deployment: %v", err)
		return
	}

	cfg.SyncStatus = "syncing"
	c.db.ID(cfg.ID).Cols("sync_status").Update(&cfg) //nolint:errcheck

	c.bus.Publish(events.SubjectDeploymentStarted, events.DeploymentEvent{ //nolint:errcheck
		DeploymentID: deploy.ID,
		EnvID:        env.ID,
		EnvName:      env.Name,
		RepoID:       deploy.RepoID,
		SHA:          deploy.SHA,
		Ref:          deploy.Ref,
		Status:       string(deploy.Status),
		TriggeredBy:  deploy.TriggeredBy,
	})

	log.Printf("gitops: triggered sync deploy %d for env %d (%s)", deploy.ID, cfg.EnvID, desiredSHA[:7])
}

// handleDeploymentSucceeded is called when any deployment.succeeded event fires.
// If the deployment was GitOps-triggered, it marks the config as synced.
func (c *Controller) handleDeploymentSucceeded(data []byte) {
	var evt events.DeploymentEvent
	if err := json.Unmarshal(data, &evt); err != nil {
		return
	}

	// Only act on deployments triggered by gitops.
	if evt.TriggeredBy != "gitops" {
		// Still update ActualSHA and resolve drift if this env has a GitOps config —
		// manual deployments also advance the state.
		var cfg models.GitOpsConfig
		if found, _ := c.db.Where("env_id = ?", evt.EnvID).Get(&cfg); found {
			markSynced(c.db, evt.EnvID, evt.SHA)
			log.Printf("gitops: env %d synced via manual deploy (%s)", evt.EnvID, sha7(evt.SHA))
		}
		return
	}

	markSynced(c.db, evt.EnvID, evt.SHA)
	log.Printf("gitops: env %d synced (%s)", evt.EnvID, sha7(evt.SHA))
}

// handleDeploymentFailed is called when deployment.failed fires.
// If the deployment was GitOps-triggered, it reverts SyncStatus back to drifted.
func (c *Controller) handleDeploymentFailed(data []byte) {
	var evt events.DeploymentEvent
	if err := json.Unmarshal(data, &evt); err != nil {
		return
	}
	if evt.TriggeredBy != "gitops" {
		return
	}

	var cfg models.GitOpsConfig
	if found, _ := c.db.Where("env_id = ?", evt.EnvID).Get(&cfg); !found {
		return
	}
	cfg.SyncStatus = "drifted"
	c.db.ID(cfg.ID).Cols("sync_status").Update(&cfg) //nolint:errcheck
	log.Printf("gitops: env %d sync failed — reverting to drifted", evt.EnvID)
}
