package auth

import (
	"fmt"
	"sort"
	"strings"
)

const GeneratedTypeScriptCapabilitiesPath = "frontend/dashboard/src/app/core/access/capabilities.ts"

type CapabilityEntry struct {
	Key   string
	Value string
}

func InstanceCapabilityCatalog() []CapabilityEntry {
	return []CapabilityEntry{
		{Key: "manageUsers", Value: string(PermInstanceManageUsers)},
		{Key: "viewAllSites", Value: string(PermInstanceViewAllSites)},
		{Key: "manageSettings", Value: string(PermInstanceManageSettings)},
		{Key: "viewSystem", Value: string(PermInstanceViewSystem)},
		{Key: "viewActivation", Value: string(PermInstanceViewActivation)},
		{Key: "manageSystem", Value: string(PermInstanceManageSystem)},
		{Key: "runMaintenance", Value: string(PermInstanceRunMaintenance)},
		{Key: "viewAudit", Value: string(PermInstanceViewAudit)},
		{Key: "exportAudit", Value: string(PermInstanceExportAudit)},
		{Key: "manageSiteExclusions", Value: string(PermInstanceManageSiteExclusions)},
		{Key: "manageImports", Value: string(PermInstanceManageImports)},
	}
}

func SiteCapabilityCatalog() []CapabilityEntry {
	return []CapabilityEntry{
		{Key: "view", Value: string(PermSiteView)},
		{Key: "manageData", Value: string(PermSiteManageData)},
		{Key: "manageGoals", Value: string(PermSiteManageGoals)},
		{Key: "manageTeam", Value: string(PermSiteManageTeam)},
		{Key: "delete", Value: string(PermSiteDelete)},
	}
}

func TeamCapabilityCatalog() []CapabilityEntry {
	return []CapabilityEntry{
		{Key: "viewMembers", Value: string(CapTeamViewMembers)},
		{Key: "manageMembers", Value: string(CapTeamManageMembers)},
		{Key: "viewAudit", Value: string(CapTeamViewAudit)},
		{Key: "manageSettings", Value: string(CapTeamManageSettings)},
		{Key: "manageApiClients", Value: string(CapTeamManageAPIClients)},
		{Key: "manageIntegrations", Value: string(CapTeamManageIntegrations)},
		{Key: "transferOwnership", Value: string(CapTeamTransferOwnership)},
		{Key: "archive", Value: string(CapTeamArchive)},
	}
}

func RenderTypeScriptCapabilities() string {
	capabilityRefs := map[string]string{}
	for group, entries := range map[string][]CapabilityEntry{
		"INSTANCE_CAPABILITIES": InstanceCapabilityCatalog(),
		"SITE_CAPABILITIES":     SiteCapabilityCatalog(),
		"TEAM_CAPABILITIES":     TeamCapabilityCatalog(),
	} {
		for _, entry := range entries {
			capabilityRefs[entry.Value] = fmt.Sprintf("%s.%s", group, entry.Key)
		}
	}

	var b strings.Builder
	b.WriteString("// Code generated by go run ./cmd/auth-capabilities --write; DO NOT EDIT.\n\n")
	writeCapabilityConst(&b, "INSTANCE_CAPABILITIES", InstanceCapabilityCatalog())
	writeCapabilityConst(&b, "SITE_CAPABILITIES", SiteCapabilityCatalog())
	writeCapabilityConst(&b, "TEAM_CAPABILITIES", TeamCapabilityCatalog())

	b.WriteString("export type InstanceCapability = (typeof INSTANCE_CAPABILITIES)[keyof typeof INSTANCE_CAPABILITIES];\n")
	b.WriteString("export type SiteCapability = (typeof SITE_CAPABILITIES)[keyof typeof SITE_CAPABILITIES];\n")
	b.WriteString("export type TeamCapability = (typeof TEAM_CAPABILITIES)[keyof typeof TEAM_CAPABILITIES];\n")
	b.WriteString("export type AccessCapability = InstanceCapability | SiteCapability | TeamCapability;\n\n")

	writeRoleCapabilities(&b, "INSTANCE_ROLE_CAPABILITIES", []string{string(InstanceOwner), string(InstanceAdmin), string(InstanceUser)}, func(role string) []string {
		return InstanceCapabilities(InstanceRole(role))
	}, capabilityRefs)
	writeRoleCapabilities(&b, "SITE_ROLE_CAPABILITIES", []string{string(SiteOwner), string(SiteAdmin), string(SiteEditor), string(SiteViewer)}, func(role string) []string {
		return SiteCapabilities(SiteRole(role))
	}, capabilityRefs)
	writeRoleCapabilities(&b, "TEAM_ROLE_CAPABILITIES", []string{"owner", "admin", "member"}, TeamCapabilities, capabilityRefs)

	b.WriteString("export type InstanceRole = keyof typeof INSTANCE_ROLE_CAPABILITIES;\n")
	b.WriteString("export type SiteRole = keyof typeof SITE_ROLE_CAPABILITIES;\n")
	b.WriteString("export type TeamRole = keyof typeof TEAM_ROLE_CAPABILITIES;\n")

	return strings.TrimRight(b.String(), "\n") + "\n"
}

func writeCapabilityConst(b *strings.Builder, name string, entries []CapabilityEntry) {
	fmt.Fprintf(b, "export const %s = {\n", name)
	for i, entry := range entries {
		comma := ","
		if i == len(entries)-1 {
			comma = ""
		}
		fmt.Fprintf(b, "    %s: '%s'%s\n", entry.Key, entry.Value, comma)
	}
	b.WriteString("} as const;\n\n")
}

func writeRoleCapabilities(b *strings.Builder, name string, roles []string, capabilities func(string) []string, capabilityRefs map[string]string) {
	fmt.Fprintf(b, "export const %s = {\n", name)
	for roleIndex, role := range roles {
		roleCapabilities := capabilities(role)
		comma := ","
		if roleIndex == len(roles)-1 {
			comma = ""
		}
		if len(roleCapabilities) <= 6 {
			refs := make([]string, 0, len(roleCapabilities))
			for _, capability := range roleCapabilities {
				refs = append(refs, capabilityRef(capability, capabilityRefs))
			}
			fmt.Fprintf(b, "    %s: [%s]%s\n", role, strings.Join(refs, ", "), comma)
			continue
		}
		fmt.Fprintf(b, "    %s: [\n", role)
		for capabilityIndex, capability := range roleCapabilities {
			ref := capabilityRef(capability, capabilityRefs)
			entryComma := ","
			if capabilityIndex == len(roleCapabilities)-1 {
				entryComma = ""
			}
			fmt.Fprintf(b, "        %s%s\n", ref, entryComma)
		}
		fmt.Fprintf(b, "    ]%s\n", comma)
	}
	b.WriteString("} as const satisfies Record<string, readonly AccessCapability[]>;\n\n")
}

func capabilityRef(capability string, capabilityRefs map[string]string) string {
	if ref, ok := capabilityRefs[capability]; ok {
		return ref
	}
	return fmt.Sprintf("'%s'", capability)
}

func SortedCapabilityValues(entries []CapabilityEntry) []string {
	values := make([]string, 0, len(entries))
	for _, entry := range entries {
		values = append(values, entry.Value)
	}
	sort.Strings(values)
	return values
}
