package plugins

import (
	"fmt"
	"os"
	"os/exec"
	"strings"

	"github.com/WAH-ISHAN/backlist-npm/devops/internal/config"
)

// Plugin is the interface that all plugins must implement
type Plugin interface {
	Name() string
	Init(*config.Config) error
	Build() error
	Deploy() error
	Destroy() error
	Status() (string, error)
	Validate() error
}

// BasePlugin provides common functionality for plugins
type BasePlugin struct {
	config *config.Config
}

// runCommand executes a shell command
func (b *BasePlugin) runCommand(name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Stdin = os.Stdin
	return cmd.Run()
}

// runCommandOutput executes a command and returns output
func (b *BasePlugin) runCommandOutput(name string, args ...string) (string, error) {
	cmd := exec.Command(name, args...)
	output, err := cmd.CombinedOutput()
	return string(output), err
}

// checkTool checks if a tool is available
func (b *BasePlugin) checkTool(name string) bool {
	_, err := exec.LookPath(name)
	return err == nil
}

// DockerPlugin handles Docker operations
type DockerPlugin struct {
	BasePlugin
}

// NewDockerPlugin creates a new Docker plugin
func NewDockerPlugin() *DockerPlugin {
	return &DockerPlugin{
		BasePlugin: BasePlugin{},
	}
}

// Name returns the plugin name
func (p *DockerPlugin) Name() string {
	return "docker"
}

// Init initializes the Docker plugin
func (p *DockerPlugin) Init(cfg *config.Config) error {
	p.config = cfg
	if !p.checkTool("docker") {
		return fmt.Errorf("docker not found")
	}
	return nil
}

// Build builds a Docker image
func (p *DockerPlugin) Build() error {
	if p.config == nil {
		return fmt.Errorf("plugin not initialized")
	}

	image := p.config.Docker.Image
	if image == "" {
		image = "myapp"
	}
	tag := p.config.Docker.Tag
	if tag == "" {
		tag = "latest"
	}
	fullImage := fmt.Sprintf("%s:%s", image, tag)

	context := p.config.Docker.Context
	if context == "" {
		context = "."
	}
	dockerfile := p.config.Docker.Dockerfile
	if dockerfile == "" {
		dockerfile = "Dockerfile"
	}

	return p.runCommand("docker", "build", "-t", fullImage, "-f", dockerfile, context)
}

// Deploy starts a Docker container
func (p *DockerPlugin) Deploy() error {
	if p.config == nil {
		return fmt.Errorf("plugin not initialized")
	}

	image := p.config.GetFullImageName()
	port := "8080:8080"

	return p.runCommand("docker", "run", "-d", "-p", port, "--name", p.config.Project.Name, image)
}

// Destroy stops and removes a Docker container
func (p *DockerPlugin) Destroy() error {
	if p.config == nil {
		return fmt.Errorf("plugin not initialized")
	}

	p.runCommand("docker", "stop", p.config.Project.Name)
	return p.runCommand("docker", "rm", p.config.Project.Name)
}

// Status returns the status of Docker resources
func (p *DockerPlugin) Status() (string, error) {
	output, err := p.runCommandOutput("docker", "ps", "-a", "--filter", fmt.Sprintf("name=%s", p.config.Project.Name))
	return strings.TrimSpace(output), err
}

// Validate checks if Docker is available
func (p *DockerPlugin) Validate() error {
	if !p.checkTool("docker") {
		return fmt.Errorf("docker is not installed")
	}
	return nil
}

// KubernetesPlugin handles Kubernetes operations
type KubernetesPlugin struct {
	BasePlugin
}

// NewKubernetesPlugin creates a new Kubernetes plugin
func NewKubernetesPlugin() *KubernetesPlugin {
	return &KubernetesPlugin{
		BasePlugin: BasePlugin{},
	}
}

// Name returns the plugin name
func (p *KubernetesPlugin) Name() string {
	return "kubernetes"
}

// Init initializes the Kubernetes plugin
func (p *KubernetesPlugin) Init(cfg *config.Config) error {
	p.config = cfg
	if !p.checkTool("kubectl") {
		return fmt.Errorf("kubectl not found")
	}
	return nil
}

// Build builds and pushes Docker image
func (p *KubernetesPlugin) Build() error {
	docker := &DockerPlugin{}
	if err := docker.Init(p.config); err != nil {
		return err
	}
	return docker.Build()
}

// Deploy deploys to Kubernetes
func (p *KubernetesPlugin) Deploy() error {
	if p.config == nil {
		return fmt.Errorf("plugin not initialized")
	}

	namespace := p.config.Kubernetes.Namespace
	if namespace == "" {
		namespace = "default"
	}

	manifests := p.config.Kubernetes.Manifests
	if len(manifests) == 0 {
		manifests = []string{"kubernetes/base"}
	}

	for _, manifest := range manifests {
		if err := p.runCommand("kubectl", "apply", "-f", manifest, "-n", namespace); err != nil {
			return err
		}
	}

	return p.runCommand("kubectl", "rollout", "status", "deployment/myapp", "-n", namespace)
}

// Destroy removes Kubernetes resources
func (p *KubernetesPlugin) Destroy() error {
	if p.config == nil {
		return fmt.Errorf("plugin not initialized")
	}

	namespace := p.config.Kubernetes.Namespace
	if namespace == "" {
		namespace = "default"
	}

	manifests := p.config.Kubernetes.Manifests
	if len(manifests) == 0 {
		manifests = []string{"kubernetes/base"}
	}

	for _, manifest := range manifests {
		if err := p.runCommand("kubectl", "delete", "-f", manifest, "-n", namespace); err != nil {
			return err
		}
	}

	return nil
}

// Status returns Kubernetes status
func (p *KubernetesPlugin) Status() (string, error) {
	namespace := "default"
	if p.config != nil && p.config.Kubernetes.Namespace != "" {
		namespace = p.config.Kubernetes.Namespace
	}
	return p.runCommandOutput("kubectl", "get", "all", "-n", namespace)
}

// Validate checks if kubectl is available
func (p *KubernetesPlugin) Validate() error {
	if !p.checkTool("kubectl") {
		return fmt.Errorf("kubectl is not installed")
	}
	return nil
}

// HelmPlugin handles Helm operations
type HelmPlugin struct {
	BasePlugin
}

// NewHelmPlugin creates a new Helm plugin
func NewHelmPlugin() *HelmPlugin {
	return &HelmPlugin{
		BasePlugin: BasePlugin{},
	}
}

// Name returns the plugin name
func (p *HelmPlugin) Name() string {
	return "helm"
}

// Init initializes the Helm plugin
func (p *HelmPlugin) Init(cfg *config.Config) error {
	p.config = cfg
	if !p.checkTool("helm") {
		return fmt.Errorf("helm not found")
	}
	return nil
}

// Build builds Helm chart
func (p *HelmPlugin) Build() error {
	if p.config == nil {
		return fmt.Errorf("plugin not initialized")
	}

	chart := p.config.Helm.Chart
	if chart == "" {
		chart = "./charts/myapp"
	}

	return p.runCommand("helm", "package", chart)
}

// Deploy deploys a Helm chart
func (p *HelmPlugin) Deploy() error {
	if p.config == nil {
		return fmt.Errorf("plugin not initialized")
	}

	chart := p.config.Helm.Chart
	if chart == "" {
		chart = "./charts/myapp"
	}

	release := p.config.Helm.Release
	if release == "" {
		release = "myapp"
	}

	namespace := p.config.Helm.Namespace
	if namespace == "" {
		namespace = "default"
	}

	args := []string{"upgrade", "--install", release, chart, "-n", namespace}

	if p.config.Helm.Wait {
		args = append(args, "--wait")
	}
	if p.config.Helm.Atomic {
		args = append(args, "--atomic")
	}
	if p.config.Helm.Timeout != "" {
		args = append(args, "--timeout", p.config.Helm.Timeout)
	}

	return p.runCommand("helm", args...)
}

// Destroy removes a Helm release
func (p *HelmPlugin) Destroy() error {
	if p.config == nil {
		return fmt.Errorf("plugin not initialized")
	}

	release := p.config.Helm.Release
	if release == "" {
		release = "myapp"
	}

	namespace := p.config.Helm.Namespace
	if namespace == "" {
		namespace = "default"
	}

	return p.runCommand("helm", "uninstall", release, "-n", namespace)
}

// Status returns Helm release status
func (p *HelmPlugin) Status() (string, error) {
	namespace := "default"
	if p.config != nil && p.config.Helm.Namespace != "" {
		namespace = p.config.Helm.Namespace
	}

	release := "myapp"
	if p.config != nil && p.config.Helm.Release != "" {
		release = p.config.Helm.Release
	}

	return p.runCommandOutput("helm", "status", release, "-n", namespace)
}

// Validate checks if Helm is available
func (p *HelmPlugin) Validate() error {
	if !p.checkTool("helm") {
		return fmt.Errorf("helm is not installed")
	}
	return nil
}

// TerraformPlugin handles Terraform operations
type TerraformPlugin struct {
	BasePlugin
}

// NewTerraformPlugin creates a new Terraform plugin
func NewTerraformPlugin() *TerraformPlugin {
	return &TerraformPlugin{
		BasePlugin: BasePlugin{},
	}
}

// Name returns the plugin name
func (p *TerraformPlugin) Name() string {
	return "terraform"
}

// Init initializes the Terraform plugin
func (p *TerraformPlugin) Init(cfg *config.Config) error {
	p.config = cfg
	if !p.checkTool("terraform") {
		return fmt.Errorf("terraform not found")
	}
	return nil
}

// Build initializes Terraform
func (p *TerraformPlugin) Build() error {
	return p.runCommand("terraform", "init")
}

// Deploy applies Terraform configuration
func (p *TerraformPlugin) Deploy() error {
	return p.runCommand("terraform", "apply", "-auto-approve")
}

// Destroy destroys Terraform resources
func (p *TerraformPlugin) Destroy() error {
	return p.runCommand("terraform", "destroy", "-auto-approve")
}

// Status shows Terraform state
func (p *TerraformPlugin) Status() (string, error) {
	return p.runCommandOutput("terraform", "show")
}

// Validate checks if Terraform is available
func (p *TerraformPlugin) Validate() error {
	if !p.checkTool("terraform") {
		return fmt.Errorf("terraform is not installed")
	}
	return nil
}

// GitHubPlugin handles GitHub Actions operations
type GitHubPlugin struct {
	BasePlugin
}

// NewGitHubPlugin creates a new GitHub plugin
func NewGitHubPlugin() *GitHubPlugin {
	return &GitHubPlugin{
		BasePlugin: BasePlugin{},
	}
}

// Name returns the plugin name
func (p *GitHubPlugin) Name() string {
	return "github"
}

// Init initializes the GitHub plugin
func (p *GitHubPlugin) Init(cfg *config.Config) error {
	p.config = cfg
	if !p.checkTool("gh") {
		return fmt.Errorf("gh (GitHub CLI) not found")
	}
	return nil
}

// Build runs tests
func (p *GitHubPlugin) Build() error {
	return p.runCommand("go", "test", "./...")
}

// Deploy triggers GitHub Actions workflow
func (p *GitHubPlugin) Deploy() error {
	workflow := "deploy.yml"
	if p.config != nil && p.config.CI.Workflow != "" {
		workflow = p.config.CI.Workflow
	}
	return p.runCommand("gh", "workflow", "run", workflow)
}

// Destroy cancels running workflows
func (p *GitHubPlugin) Destroy() error {
	return p.runCommand("gh", "workflow", "cancel", "deploy.yml")
}

// Status returns workflow status
func (p *GitHubPlugin) Status() (string, error) {
	return p.runCommandOutput("gh", "run", "list")
}

// Validate checks if gh is available
func (p *GitHubPlugin) Validate() error {
	if !p.checkTool("gh") {
		return fmt.Errorf("gh (GitHub CLI) is not installed")
	}
	return nil
}

// GitLabPlugin handles GitLab CI operations
type GitLabPlugin struct {
	BasePlugin
}

// NewGitLabPlugin creates a new GitLab plugin
func NewGitLabPlugin() *GitLabPlugin {
	return &GitLabPlugin{
		BasePlugin: BasePlugin{},
	}
}

// Name returns the plugin name
func (p *GitLabPlugin) Name() string {
	return "gitlab"
}

// Init initializes the GitLab plugin
func (p *GitLabPlugin) Init(cfg *config.Config) error {
	p.config = cfg
	if !p.checkTool("glab") {
		return fmt.Errorf("glab (GitLab CLI) not found")
	}
	return nil
}

// Build runs tests
func (p *GitLabPlugin) Build() error {
	return p.runCommand("go", "test", "./...")
}

// Deploy triggers GitLab CI pipeline
func (p *GitLabPlugin) Deploy() error {
	return p.runCommand("glab", "ci", "run", "--branch", "main")
}

// Destroy cancels running pipelines
func (p *GitLabPlugin) Destroy() error {
	return p.runCommand("glab", "ci", "cancel")
}

// Status returns pipeline status
func (p *GitLabPlugin) Status() (string, error) {
	return p.runCommandOutput("glab", "ci", "list")
}

// Validate checks if glab is available
func (p *GitLabPlugin) Validate() error {
	if !p.checkTool("glab") {
		return fmt.Errorf("glab (GitLab CLI) is not installed")
	}
	return nil
}

// JenkinsPlugin handles Jenkins operations
type JenkinsPlugin struct {
	BasePlugin
}

// NewJenkinsPlugin creates a new Jenkins plugin
func NewJenkinsPlugin() *JenkinsPlugin {
	return &JenkinsPlugin{
		BasePlugin: BasePlugin{},
	}
}

// Name returns the plugin name
func (p *JenkinsPlugin) Name() string {
	return "jenkins"
}

// Init initializes the Jenkins plugin
func (p *JenkinsPlugin) Init(cfg *config.Config) error {
	p.config = cfg
	return nil
}

// Build runs tests via Jenkins
func (p *JenkinsPlugin) Build() error {
	return p.runCommand("jenkins-cli", "build", "myapp")
}

// Deploy deploys via Jenkins
func (p *JenkinsPlugin) Deploy() error {
	return p.runCommand("jenkins-cli", "build", "myapp-deploy")
}

// Destroy cancels builds
func (p *JenkinsPlugin) Destroy() error {
	return p.runCommand("jenkins-cli", "cancel", "myapp")
}

// Status returns build status
func (p *JenkinsPlugin) Status() (string, error) {
	return p.runCommandOutput("jenkins-cli", "list-jobs")
}

// Validate validates Jenkins configuration
func (p *JenkinsPlugin) Validate() error {
	return nil
}

// AWSPlugin handles AWS operations
type AWSPlugin struct {
	BasePlugin
}

// NewAWSPlugin creates a new AWS plugin
func NewAWSPlugin() *AWSPlugin {
	return &AWSPlugin{
		BasePlugin: BasePlugin{},
	}
}

// Name returns the plugin name
func (p *AWSPlugin) Name() string {
	return "aws"
}

// Init initializes the AWS plugin
func (p *AWSPlugin) Init(cfg *config.Config) error {
	p.config = cfg
	if !p.checkTool("aws") {
		return fmt.Errorf("aws CLI not found")
	}
	return nil
}

// Build builds in AWS
func (p *AWSPlugin) Build() error {
	return p.runCommand("aws", "ecr", "get-login-password", "--region", p.config.Cloud.Region)
}

// Deploy deploys to AWS
func (p *AWSPlugin) Deploy() error {
	return nil
}

// Destroy destroys AWS resources
func (p *AWSPlugin) Destroy() error {
	return nil
}

// Status returns AWS status
func (p *AWSPlugin) Status() (string, error) {
	return p.runCommandOutput("aws", "sts", "get-caller-identity")
}

// Validate checks AWS CLI
func (p *AWSPlugin) Validate() error {
	if !p.checkTool("aws") {
		return fmt.Errorf("AWS CLI is not installed")
	}
	return nil
}

// AzurePlugin handles Azure operations
type AzurePlugin struct {
	BasePlugin
}

// NewAzurePlugin creates a new Azure plugin
func NewAzurePlugin() *AzurePlugin {
	return &AzurePlugin{
		BasePlugin: BasePlugin{},
	}
}

// Name returns the plugin name
func (p *AzurePlugin) Name() string {
	return "azure"
}

// Init initializes the Azure plugin
func (p *AzurePlugin) Init(cfg *config.Config) error {
	p.config = cfg
	if !p.checkTool("az") {
		return fmt.Errorf("az CLI not found")
	}
	return nil
}

// Build builds in Azure
func (p *AzurePlugin) Build() error {
	return p.runCommand("az", "account", "show")
}

// Deploy deploys to Azure
func (p *AzurePlugin) Deploy() error {
	return nil
}

// Destroy destroys Azure resources
func (p *AzurePlugin) Destroy() error {
	return nil
}

// Status returns Azure status
func (p *AzurePlugin) Status() (string, error) {
	return p.runCommandOutput("az", "account", "show")
}

// Validate checks Azure CLI
func (p *AzurePlugin) Validate() error {
	if !p.checkTool("az") {
		return fmt.Errorf("Azure CLI is not installed")
	}
	return nil
}

// GCPPlugin handles GCP operations
type GCPPlugin struct {
	BasePlugin
}

// NewGCPPlugin creates a new GCP plugin
func NewGCPPlugin() *GCPPlugin {
	return &GCPPlugin{
		BasePlugin: BasePlugin{},
	}
}

// Name returns the plugin name
func (p *GCPPlugin) Name() string {
	return "gcp"
}

// Init initializes the GCP plugin
func (p *GCPPlugin) Init(cfg *config.Config) error {
	p.config = cfg
	if !p.checkTool("gcloud") {
		return fmt.Errorf("gcloud CLI not found")
	}
	return nil
}

// Build builds in GCP
func (p *GCPPlugin) Build() error {
	return p.runCommand("gcloud", "auth", "list")
}

// Deploy deploys to GCP
func (p *GCPPlugin) Deploy() error {
	return nil
}

// Destroy destroys GCP resources
func (p *GCPPlugin) Destroy() error {
	return nil
}

// Status returns GCP status
func (p *GCPPlugin) Status() (string, error) {
	return p.runCommandOutput("gcloud", "info")
}

// Validate checks gcloud CLI
func (p *GCPPlugin) Validate() error {
	if !p.checkTool("gcloud") {
		return fmt.Errorf("gcloud CLI is not installed")
	}
	return nil
}
