import { CommonModule } from '@angular/common';
import {
  Component,
  EventEmitter,
  Output,
  computed,
  inject,
  input,
  signal
} from '@angular/core';
import { Router } from '@angular/router';
import { NgIcon, provideIcons } from '@ng-icons/core';
import {
  lucideArrowLeft,
  lucideBug,
  lucideCheck,
  lucidePackage,
  lucidePlay,
  lucideRefreshCw,
  lucideSettings,
  lucideShield,
  lucideStore,
  lucideX
} from '@ng-icons/lucide';
import type { PluginCapabilityId, TojuPluginInstallScope } from '../../../../shared-kernel';
import { AppI18nService, APP_TRANSLATE_IMPORTS } from '../../../../core/i18n';
import { PluginCapabilityService } from '../../application/services/plugin-capability.service';
import { PluginHostService } from '../../application/services/plugin-host.service';
import { PluginLoggerService } from '../../application/services/plugin-logger.service';
import { PluginRegistryService } from '../../application/services/plugin-registry.service';
import { PluginRequirementStateService } from '../../application/services/plugin-requirement-state.service';
import { PluginUiRegistryService } from '../../application/services/plugin-ui-registry.service';
import { getPluginInstallScope } from '../../domain/logic/plugin-install-scope.logic';
import type { RegisteredPlugin } from '../../domain/models/plugin-runtime.models';
import { PluginRenderHostComponent } from '../plugin-render-host/plugin-render-host.component';

type PluginManagerTab = 'docs' | 'extensions' | 'installed' | 'logs' | 'requirements' | 'settings';

@Component({
  selector: 'app-plugin-manager',
  standalone: true,
  imports: [
    CommonModule,
    NgIcon,
    PluginRenderHostComponent,
    ...APP_TRANSLATE_IMPORTS
  ],
  templateUrl: './plugin-manager.component.html',
  viewProviders: [
    provideIcons({
      lucideArrowLeft,
      lucideBug,
      lucideCheck,
      lucidePackage,
      lucidePlay,
      lucideRefreshCw,
      lucideSettings,
      lucideShield,
      lucideStore,
      lucideX
    })
  ]
})
export class PluginManagerComponent {
  @Output() readonly closed = new EventEmitter<void>();
  @Output() readonly storeOpened = new EventEmitter<void>();

  readonly scope = input<TojuPluginInstallScope>('client');

  readonly capabilities = inject(PluginCapabilityService);
  readonly host = inject(PluginHostService);
  readonly logger = inject(PluginLoggerService);
  readonly registry = inject(PluginRegistryService);
  readonly requirementState = inject(PluginRequirementStateService);
  readonly router = inject(Router);
  readonly uiRegistry = inject(PluginUiRegistryService);
  private readonly appI18n = inject(AppI18nService);
  readonly activeTab = signal<PluginManagerTab>('installed');
  readonly busyPluginId = signal<string | null>(null);
  readonly busyAll = signal(false);
  readonly selectedPluginId = signal<string | null>(null);
  readonly allEntries = this.registry.entries;
  readonly entries = computed(() => this.allEntries().filter((entry) => this.entryBelongsToScope(entry)));
  readonly managerTitle = computed(() => this.scope() === 'server'
    ? this.appI18n.instant('plugins.manager.serverTitle')
    : this.appI18n.instant('plugins.manager.clientTitle'));
  readonly managerDescription = computed(() => this.scope() === 'server'
    ? this.appI18n.instant('plugins.manager.serverDescription')
    : this.appI18n.instant('plugins.manager.clientDescription'));
  readonly selectedPlugin = computed(() => {
    const selectedPluginId = this.selectedPluginId();

    return this.entries().find((entry) => entry.manifest.id === selectedPluginId) ?? this.entries()[0] ?? null;
  });
  readonly missingCapabilities = computed(() => {
    const selectedPlugin = this.selectedPlugin();

    return selectedPlugin ? this.capabilities.missing(selectedPlugin.manifest) : [];
  });
  readonly selectedLogs = computed(() => {
    const selectedPlugin = this.selectedPlugin();

    return selectedPlugin ? this.logger.entries().filter((entry) => entry.pluginId === selectedPlugin.manifest.id)
      .slice(-20) : [];
  });
  readonly extensionCounts = computed(() => ({
    appPages: this.uiRegistry.appPageRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length,
    channelSections: this.uiRegistry.channelSectionRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length,
    composerActions: this.uiRegistry.composerActionRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length,
    embeds: this.uiRegistry.embedRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length,
    profileActions: this.uiRegistry.profileActionRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length,
    settingsPages: this.uiRegistry.settingsPageRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length,
    sidePanels: this.uiRegistry.sidePanelRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length,
    slashCommands: this.uiRegistry.slashCommandRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length,
    toolbarActions: this.uiRegistry.toolbarActionRecords().filter((record) => this.hasVisiblePlugin(record.pluginId)).length
  }));
  readonly extensionCountItems = computed(() => {
    const counts = this.extensionCounts();

    return [
      { label: this.appI18n.instant('plugins.manager.extensionCounts.settingsPages'), value: counts.settingsPages },
      { label: this.appI18n.instant('plugins.manager.extensionCounts.appPages'), value: counts.appPages },
      { label: this.appI18n.instant('plugins.manager.extensionCounts.sidePanels'), value: counts.sidePanels },
      { label: this.appI18n.instant('plugins.manager.extensionCounts.channelSections'), value: counts.channelSections },
      { label: this.appI18n.instant('plugins.manager.extensionCounts.composerActions'), value: counts.composerActions },
      { label: this.appI18n.instant('plugins.manager.extensionCounts.profileActions'), value: counts.profileActions },
      { label: this.appI18n.instant('plugins.manager.extensionCounts.toolbarActions'), value: counts.toolbarActions },
      { label: this.appI18n.instant('plugins.manager.extensionCounts.slashCommands'), value: counts.slashCommands },
      { label: this.appI18n.instant('plugins.manager.extensionCounts.embedRenderers'), value: counts.embeds }
    ];
  });
  readonly requirementComparisons = computed(() => this.scope() === 'server' ? this.requirementState.comparisons() : []);
  readonly uiConflicts = computed(() => this.uiRegistry.conflicts()
    .filter((conflict) => conflict.pluginIds.some((pluginId) => this.hasVisiblePlugin(pluginId))));
  readonly selectedRequirement = computed(() => {
    const selectedPlugin = this.selectedPlugin();

    return selectedPlugin ? this.requirementState.comparisonFor(selectedPlugin.manifest.id) : null;
  });
  readonly selectedSettingsSchema = computed(() => this.selectedPlugin()?.manifest.settings ?? null);
  readonly selectedSettingsPages = computed(() => {
    const selectedPlugin = this.selectedPlugin();

    return selectedPlugin
      ? this.uiRegistry.settingsPageRecords().filter((record) => record.pluginId === selectedPlugin.manifest.id)
      : [];
  });
  readonly emptyTitle = computed(() => this.scope() === 'server'
    ? this.appI18n.instant('plugins.manager.empty.serverTitle')
    : this.appI18n.instant('plugins.manager.empty.clientTitle'));
  readonly emptyBody = computed(() => this.scope() === 'server'
    ? this.appI18n.instant('plugins.manager.empty.serverBody')
    : this.appI18n.instant('plugins.manager.empty.clientBody'));
  readonly selectedDocs = computed(() => {
    const manifest = this.selectedPlugin()?.manifest;

    if (!manifest) {
      return [];
    }

    return [
      { label: this.appI18n.instant('plugins.manager.docs.readme'), url: manifest.readme },
      { label: this.appI18n.instant('plugins.manager.docs.homepage'), url: manifest.homepage },
      { label: this.appI18n.instant('plugins.manager.docs.changelog'), url: manifest.changelog },
      { label: this.appI18n.instant('plugins.manager.docs.support'), url: manifest.bugs }
    ].filter((item): item is { label: string; url: string } => typeof item.url === 'string' && item.url.length > 0);
  });

  setTab(tab: PluginManagerTab): void {
    this.activeTab.set(tab);
  }

  openStore(): void {
    const returnUrl = this.router.url.startsWith('/plugin-store') ? '/dashboard' : this.router.url;

    this.storeOpened.emit();
    void this.router.navigate(['/plugin-store'], { queryParams: { returnUrl } });
  }

  selectPlugin(pluginId: string): void {
    this.selectedPluginId.set(pluginId);
  }

  grantAll(entry: RegisteredPlugin): void {
    this.capabilities.grantAll(entry.manifest);
  }

  toggleCapability(entry: RegisteredPlugin, capability: PluginCapabilityId): void {
    if (this.capabilities.has(entry.manifest.id, capability)) {
      this.capabilities.revoke(entry.manifest.id, capability);
      return;
    }

    this.capabilities.grant(entry.manifest.id, capability);
  }

  async activateAll(): Promise<void> {
    this.busyAll.set(true);

    try {
      await this.host.activateReadyPlugins();
    } finally {
      this.busyAll.set(false);
    }
  }

  async reload(entry: RegisteredPlugin): Promise<void> {
    this.busyPluginId.set(entry.manifest.id);

    try {
      await this.host.reloadPlugin(entry.manifest.id);
    } finally {
      this.busyPluginId.set(null);
    }
  }

  async activate(entry: RegisteredPlugin): Promise<void> {
    this.busyPluginId.set(entry.manifest.id);

    try {
      await this.host.activatePluginById(entry.manifest.id);
    } finally {
      this.busyPluginId.set(null);
    }
  }

  async unload(entry: RegisteredPlugin): Promise<void> {
    this.busyPluginId.set(entry.manifest.id);

    try {
      await this.host.deactivatePlugin(entry.manifest.id, { forgetActivation: true });
    } finally {
      this.busyPluginId.set(null);
    }
  }

  setEnabled(entry: RegisteredPlugin, enabled: boolean): void {
    this.registry.setEnabled(entry.manifest.id, enabled);
  }

  isSelected(entry: RegisteredPlugin): boolean {
    return this.selectedPlugin()?.manifest.id === entry.manifest.id;
  }

  isActive(entry: RegisteredPlugin): boolean {
    return this.host.isPluginActive(entry.manifest.id);
  }

  close(): void {
    this.closed.emit();
  }

  trackEntry(index: number, entry: RegisteredPlugin): string {
    return entry.manifest.id;
  }

  trackCapability(index: number, capability: PluginCapabilityId): string {
    return capability;
  }

  private entryBelongsToScope(entry: RegisteredPlugin): boolean {
    return getPluginInstallScope(entry.manifest) === this.scope();
  }

  private hasVisiblePlugin(pluginId: string): boolean {
    return this.entries().some((entry) => entry.manifest.id === pluginId);
  }
}
