package bootstrap

import (
	"context"
	"fmt"

	"haixun-backend/internal/config"
	libcrypto "haixun-backend/internal/library/crypto"
	libmongo "haixun-backend/internal/library/mongo"
	brandrepo "haixun-backend/internal/model/brand/repository"
	cmatrixrepo "haixun-backend/internal/model/content_matrix/repository"
	copydraftrepo "haixun-backend/internal/model/copy_draft/repository"
	copymissionrepo "haixun-backend/internal/model/copy_mission/repository"
	jobrepo "haixun-backend/internal/model/job/repository"
	kgrepo "haixun-backend/internal/model/knowledge_graph/repository"
	memberrepo "haixun-backend/internal/model/member/repository"
	outreachdraftrepo "haixun-backend/internal/model/outreach_draft/repository"
	permissionrepo "haixun-backend/internal/model/permission/repository"
	permissionuc "haixun-backend/internal/model/permission/usecase"
	personarepo "haixun-backend/internal/model/persona/repository"
	placementtopicrepo "haixun-backend/internal/model/placement_topic/repository"
	scanpostrepo "haixun-backend/internal/model/scan_post/repository"
	settingrepo "haixun-backend/internal/model/setting/repository"
	threadsaccountrepo "haixun-backend/internal/model/threads_account/repository"
)

type InitOptions struct {
	TenantID    string
	AdminEmail  string
	AdminPass   string
	DisplayName string
}

type InitReport struct {
	IndexesEnsured        bool
	PermissionsSeeded     bool
	RolePermissionsSeeded bool
	AdminUID              string
	AdminCreated          bool
}

func Init(ctx context.Context, cfg config.Config, opts InitOptions) (*InitReport, error) {
	if cfg.Mongo.URI == "" || cfg.Mongo.Database == "" {
		return nil, fmt.Errorf("mongo URI and database are required")
	}
	if opts.TenantID == "" {
		return nil, fmt.Errorf("tenant_id is required")
	}
	if opts.AdminEmail == "" || opts.AdminPass == "" {
		return nil, fmt.Errorf("admin email and password are required")
	}

	mongoClient, err := libmongo.NewClient(ctx, cfg.Mongo)
	if err != nil {
		return nil, fmt.Errorf("connect mongo: %w", err)
	}
	defer func() { _ = mongoClient.Close(ctx) }()

	db := mongoClient.Database()
	report := &InitReport{}

	// cipher 只用於資料加解密；EnsureIndexes 不會用到，但 secrets repo 建構子需要它。
	secretsCipher, err := libcrypto.New(cfg.Secrets.EncryptionKey)
	if err != nil {
		return nil, fmt.Errorf("init secrets cipher: %w", err)
	}

	settingRepository := settingrepo.NewMongoRepository(db)
	memberRepository := memberrepo.NewMongoRepository(db)
	permissionRepository := permissionrepo.NewMongoPermissionRepository(db)
	rolePermissionRepository := permissionrepo.NewMongoRolePermissionRepository(db)
	jobTemplateRepository := jobrepo.NewMongoTemplateRepository(db)
	jobRunRepository := jobrepo.NewMongoRunRepository(db)
	jobScheduleRepository := jobrepo.NewMongoScheduleRepository(db)
	jobEventRepository := jobrepo.NewMongoEventRepository(db)

	repos := []struct {
		name string
		fn   func(context.Context) error
	}{
		{"settings", settingRepository.EnsureIndexes},
		{"members", memberRepository.EnsureIndexes},
		{"permissions", permissionRepository.EnsureIndexes},
		{"role_permissions", rolePermissionRepository.EnsureIndexes},
		{"job_templates", jobTemplateRepository.EnsureIndexes},
		{"job_runs", jobRunRepository.EnsureIndexes},
		{"job_schedules", jobScheduleRepository.EnsureIndexes},
		{"job_events", jobEventRepository.EnsureIndexes},
		{"copy_missions", copymissionrepo.NewMongoRepository(db).EnsureIndexes},
		{"copy_drafts", copydraftrepo.NewMongoRepository(db).EnsureIndexes},
		{"scan_posts", scanpostrepo.NewMongoRepository(db).EnsureIndexes},
		{"outreach_drafts", outreachdraftrepo.NewMongoRepository(db).EnsureIndexes},
		{"content_matrix", cmatrixrepo.NewMongoRepository(db).EnsureIndexes},
		{"knowledge_graph", kgrepo.NewMongoRepository(db).EnsureIndexes},
		{"personas", personarepo.NewMongoRepository(db).EnsureIndexes},
		{"brands", brandrepo.NewMongoRepository(db).EnsureIndexes},
		{"placement_topics", placementtopicrepo.NewMongoRepository(db).EnsureIndexes},
		{"threads_accounts", threadsaccountrepo.NewMongoRepository(db).EnsureIndexes},
		{"threads_account_secrets", threadsaccountrepo.NewSecretsMongoRepository(db, secretsCipher).EnsureIndexes},
	}
	for _, repo := range repos {
		if err := repo.fn(ctx); err != nil {
			return nil, fmt.Errorf("ensure %s indexes: %w", repo.name, err)
		}
	}
	report.IndexesEnsured = true

	permissionUseCase := permissionuc.NewUseCase(permissionRepository, rolePermissionRepository)
	if err := permissionUseCase.EnsureDefaultPermissions(ctx); err != nil {
		return nil, fmt.Errorf("seed permissions catalog: %w", err)
	}
	report.PermissionsSeeded = true

	if err := permissionUseCase.EnsureDefaultRolePermissions(ctx, opts.TenantID); err != nil {
		return nil, fmt.Errorf("seed role permissions: %w", err)
	}
	report.RolePermissionsSeeded = true

	admin, created, err := EnsureAdminMember(ctx, memberRepository, AdminOptions{
		TenantID:    opts.TenantID,
		Email:       opts.AdminEmail,
		Password:    opts.AdminPass,
		DisplayName: opts.DisplayName,
	})
	if err != nil {
		return nil, err
	}
	report.AdminUID = admin.UID
	report.AdminCreated = created

	return report, nil
}
