package main

import (
	"baron-sso-backend/internal/bootstrap"
	"baron-sso-backend/internal/idp"
	"baron-sso-backend/internal/logger"
	"baron-sso-backend/internal/repository"
	"baron-sso-backend/internal/service"
	"context"
	"flag"
	"fmt"
	"log"
	"log/slog"
	"os"
	"strings"
	"time"

	"github.com/joho/godotenv"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	gormLogger "gorm.io/gorm/logger"
)

type createSuperAdminConfig struct {
	Email          string
	Password       string
	Name           string
	UpdatePassword bool
}

type clearOrphanUserTenantMembershipsConfig struct {
	DryRun bool
}

func main() {
	loadEnv()
	logger.Init(logger.Config{
		ServiceName:   "baron-sso-adminctl",
		Environment:   getenv("APP_ENV", getenv("GO_ENV", "dev")),
		LevelOverride: getenv("BACKEND_LOG_LEVEL", ""),
	})

	if len(os.Args) < 2 {
		printUsage()
		os.Exit(2)
	}

	switch os.Args[1] {
	case "create-super-admin":
		if err := runCreateSuperAdmin(os.Args[2:]); err != nil {
			slog.Error("create-super-admin failed", "error", err)
			os.Exit(1)
		}
	case "clear-orphan-user-tenant-memberships":
		if err := runClearOrphanUserTenantMemberships(os.Args[2:]); err != nil {
			slog.Error("clear-orphan-user-tenant-memberships failed", "error", err)
			os.Exit(1)
		}
	case "worksmobile-sync":
		if err := runWorksmobileSync(os.Args[2:]); err != nil {
			slog.Error("worksmobile-sync failed", "error", err)
			os.Exit(1)
		}
	default:
		printUsage()
		os.Exit(2)
	}
}

func runCreateSuperAdmin(args []string) error {
	config, err := resolveCreateSuperAdminConfig(args)
	if err != nil {
		return err
	}

	db, err := openDB()
	if err != nil {
		return err
	}
	if err := bootstrap.Run(db); err != nil {
		return err
	}

	provider, err := idp.InitializeProvider()
	if err != nil {
		return err
	}
	if provider == nil {
		return fmt.Errorf("idp provider is required")
	}

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	result, err := bootstrap.EnsureSuperAdmin(
		ctx,
		service.NewKratosAdminService(),
		bootstrap.NewGormSuperAdminStore(db, repository.NewKetoOutboxRepository(db)),
		bootstrap.EnsureSuperAdminOptions{
			Email:          config.Email,
			Password:       config.Password,
			Name:           config.Name,
			Source:         "adminctl",
			UpdatePassword: config.UpdatePassword,
		},
	)
	if err != nil {
		return err
	}

	fmt.Printf("super admin ensured: email=%s identity_id=%s user_id=%s identity_created=%t local_created=%t local_updated=%t password_updated=%t keto_relation_queued=%t\n",
		result.Email,
		result.IdentityID,
		result.LocalUserID,
		result.IdentityCreated,
		result.LocalUserCreated,
		result.LocalUserUpdated,
		result.PasswordUpdated,
		result.KetoRelationQueued,
	)
	return nil
}

func runClearOrphanUserTenantMemberships(args []string) error {
	config, err := resolveClearOrphanUserTenantMembershipsConfig(args)
	if err != nil {
		return err
	}

	db, err := openDB()
	if err != nil {
		return err
	}

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	if config.DryRun {
		count, err := repository.CountOrphanUserTenantMemberships(ctx, db)
		if err != nil {
			return err
		}
		fmt.Printf("orphan user tenant memberships dry-run: count=%d\n", count)
		return nil
	}

	affected, err := repository.ClearOrphanUserTenantMemberships(ctx, db)
	if err != nil {
		return err
	}
	fmt.Printf("orphan user tenant memberships cleared: count=%d\n", affected)
	return nil
}

func resolveCreateSuperAdminConfig(args []string) (createSuperAdminConfig, error) {
	fs := flag.NewFlagSet("create-super-admin", flag.ContinueOnError)
	fs.SetOutput(os.Stderr)

	config := createSuperAdminConfig{}
	fs.StringVar(&config.Email, "email", getenv("ADMIN_EMAIL", ""), "admin email")
	fs.StringVar(&config.Password, "password", getenv("ADMIN_PASSWORD", ""), "admin password")
	fs.StringVar(&config.Name, "name", getenv("ADMIN_NAME", "System Admin"), "admin display name")
	fs.BoolVar(&config.UpdatePassword, "update-password", false, "update password when identity already exists")

	if err := fs.Parse(args); err != nil {
		return config, err
	}

	config.Email = strings.TrimSpace(config.Email)
	config.Name = strings.TrimSpace(config.Name)
	if config.Email == "" {
		return config, fmt.Errorf("admin email is required; pass --email or set ADMIN_EMAIL")
	}
	if strings.TrimSpace(config.Password) == "" {
		return config, fmt.Errorf("admin password is required; pass --password or set ADMIN_PASSWORD")
	}
	if config.Name == "" {
		config.Name = "System Admin"
	}
	return config, nil
}

func resolveClearOrphanUserTenantMembershipsConfig(args []string) (clearOrphanUserTenantMembershipsConfig, error) {
	fs := flag.NewFlagSet("clear-orphan-user-tenant-memberships", flag.ContinueOnError)
	fs.SetOutput(os.Stderr)

	config := clearOrphanUserTenantMembershipsConfig{}
	fs.BoolVar(&config.DryRun, "dry-run", false, "count orphan memberships without updating users")

	if err := fs.Parse(args); err != nil {
		return config, err
	}
	return config, nil
}

func openDB() (*gorm.DB, error) {
	dsn := fmt.Sprintf(
		"host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Seoul",
		getenv("DB_HOST", "localhost"),
		getenv("DB_USER", "baron"),
		getenv("DB_PASSWORD", "password"),
		getenv("DB_NAME", "baron_sso"),
		getenv("DB_PORT", "5432"),
	)
	return gorm.Open(postgres.Open(dsn), &gorm.Config{
		Logger: gormLogger.New(
			log.New(os.Stdout, "\r\n", log.LstdFlags),
			gormLogger.Config{
				SlowThreshold:             time.Second,
				LogLevel:                  gormLogger.Warn,
				IgnoreRecordNotFoundError: true,
				Colorful:                  true,
			},
		),
	})
}

func loadEnv() {
	_ = godotenv.Load(".env")
	_ = godotenv.Load("../.env")
	_ = godotenv.Load("../../.env")
}

func getenv(key string, fallback string) string {
	if value, ok := os.LookupEnv(key); ok {
		return value
	}
	return fallback
}

func printUsage() {
	fmt.Fprintln(os.Stderr, "usage:")
	fmt.Fprintln(os.Stderr, "  adminctl create-super-admin [--email EMAIL] [--password PASSWORD] [--name NAME] [--update-password]")
	fmt.Fprintln(os.Stderr, "  adminctl clear-orphan-user-tenant-memberships [--dry-run]")
	fmt.Fprintln(os.Stderr, "  adminctl worksmobile-sync [--orgunits] [--users-csv PATH] [--credential-batch-id ID] [--process] [--serialize-orgunits] [--serialize-users-batch ID] [--batch-size N] [--delay DURATION]")
}
