import { Injectable, computed, signal } from '@angular/core';

import { OrganizationOnboardingDraft } from '../models/organization-draft.model';
import {
  OnboardingStepDefinition,
  OrganizationAdminValue,
  OrganizationBasicsValue,
  OrganizationLocalizationValue,
  OrganizationOnboardingData,
  OrganizationPlanLimitsValue,
} from '../models/organization-onboarding.model';

const INITIAL_DATA: OrganizationOnboardingData = {
  basics: null,
  localization: null,
  planLimits: null,
  admin: null,
};

type OnboardingDraftState = {
  readonly basics: Partial<OrganizationBasicsValue> | null;
  readonly localization: Partial<OrganizationLocalizationValue> | null;
  readonly planLimits: Partial<OrganizationPlanLimitsValue> | null;
  readonly admin: Partial<OrganizationAdminValue> | null;
};

const INITIAL_DRAFT_STATE: OnboardingDraftState = {
  basics: null,
  localization: null,
  planLimits: null,
  admin: null,
};

@Injectable()
export class OrganizationOnboardingStateService {
  private readonly onboardingDataState = signal<OrganizationOnboardingData>(INITIAL_DATA);
  private readonly onboardingDraftState = signal<OnboardingDraftState>(INITIAL_DRAFT_STATE);
  private readonly currentStepIndexState = signal(0);
  private readonly completedStepIndexesState = signal<readonly number[]>([]);
  private readonly savedDraftIdState = signal<string | null>(null);
  private readonly savedAtState = signal<string | null>(null);

  readonly onboardingData = this.onboardingDataState.asReadonly();
  readonly onboardingDraft = this.onboardingDraftState.asReadonly();
  readonly currentStepIndex = this.currentStepIndexState.asReadonly();
  readonly completedStepIndexes = this.completedStepIndexesState.asReadonly();
  readonly savedDraftId = this.savedDraftIdState.asReadonly();
  readonly savedAt = this.savedAtState.asReadonly();

  readonly basics = computed(() => this.onboardingDataState().basics);
  readonly localization = computed(() => this.onboardingDataState().localization);
  readonly planLimits = computed(() => this.onboardingDataState().planLimits);
  readonly admin = computed(() => this.onboardingDataState().admin);
  readonly hasAnyData = computed(() => {
    const data = this.onboardingDataState();
    return !!(data.basics || data.localization || data.planLimits || data.admin);
  });

  setCurrentStepIndex(index: number): void {
    this.currentStepIndexState.set(Math.max(0, index));
  }

  updateBasics(value: OrganizationBasicsValue | null): void {
    this.onboardingDataState.update(current => ({ ...current, basics: value }));
    this.onboardingDraftState.update(current => ({ ...current, basics: value }));
  }

  updateLocalization(value: OrganizationLocalizationValue | null): void {
    this.onboardingDataState.update(current => ({ ...current, localization: value }));
    this.onboardingDraftState.update(current => ({ ...current, localization: value }));
  }

  updatePlanLimits(value: OrganizationPlanLimitsValue | null): void {
    this.onboardingDataState.update(current => ({ ...current, planLimits: value }));
    this.onboardingDraftState.update(current => ({ ...current, planLimits: value }));
  }

  updateAdmin(value: OrganizationAdminValue | null): void {
    this.onboardingDataState.update(current => ({ ...current, admin: value }));
    this.onboardingDraftState.update(current => ({ ...current, admin: value }));
  }

  updateBasicsDraft(value: Partial<OrganizationBasicsValue> | null): void {
    this.onboardingDraftState.update(current => ({ ...current, basics: value }));
  }

  updateLocalizationDraft(value: Partial<OrganizationLocalizationValue> | null): void {
    this.onboardingDraftState.update(current => ({ ...current, localization: value }));
  }

  updatePlanLimitsDraft(value: Partial<OrganizationPlanLimitsValue> | null): void {
    this.onboardingDraftState.update(current => ({ ...current, planLimits: value }));
  }

  updateAdminDraft(value: Partial<OrganizationAdminValue> | null): void {
    this.onboardingDraftState.update(current => ({ ...current, admin: value }));
  }

  markStepCompleted(index: number): void {
    this.completedStepIndexesState.update(current =>
      current.includes(index)
        ? current
        : [...current, index].sort((left, right) => left - right)
    );
  }

  unmarkStepCompleted(index: number): void {
    this.completedStepIndexesState.update(current => current.filter(item => item !== index));
  }

  setCompletedStepIndexes(indexes: readonly number[]): void {
    const uniqueIndexes = [...new Set(indexes.filter(index => index >= 0))].sort((left, right) => left - right);
    this.completedStepIndexesState.set(uniqueIndexes);
  }

  canOpenStep(index: number): boolean {
    return index <= this.currentStepIndexState() || this.completedStepIndexesState().includes(index);
  }

  isStepCompleted(index: number): boolean {
    return this.completedStepIndexesState().includes(index);
  }

  buildDraft(): OrganizationOnboardingDraft {
    const draft = this.onboardingDraftState();

    return {
      schemaVersion: 1,
      draftId: this.savedDraftIdState(),
      currentStepIndex: this.currentStepIndexState(),
      completedStepIndexes: this.completedStepIndexesState(),
      basics: draft.basics,
      localization: draft.localization,
      planLimits: draft.planLimits,
      admin: draft.admin,
      savedAt: new Date().toISOString(),
    };
  }

  restoreDraft(draft: OrganizationOnboardingDraft): void {
    this.onboardingDraftState.set({
      basics: draft.basics ? { ...draft.basics } : null,
      localization: draft.localization ? { ...draft.localization } : null,
      planLimits: draft.planLimits ? { ...draft.planLimits } : null,
      admin: draft.admin ? { ...draft.admin } : null,
    });
    this.onboardingDataState.set({
      basics: draft.completedStepIndexes.includes(0) && draft.basics
        ? { ...draft.basics } as OrganizationBasicsValue
        : null,
      localization: draft.completedStepIndexes.includes(1) && draft.localization
        ? { ...draft.localization } as OrganizationLocalizationValue
        : null,
      planLimits: draft.completedStepIndexes.includes(2) && draft.planLimits
        ? { ...draft.planLimits } as OrganizationPlanLimitsValue
        : null,
      admin: draft.completedStepIndexes.includes(3) && draft.admin
        ? { ...draft.admin } as OrganizationAdminValue
        : null,
    });
    this.currentStepIndexState.set(Math.max(0, draft.currentStepIndex));
    this.setCompletedStepIndexes(draft.completedStepIndexes);
    this.savedDraftIdState.set(draft.draftId);
    this.savedAtState.set(draft.savedAt);
  }

  updateSavedDraftMetadata(draftId: string | null, savedAt: string): void {
    this.savedDraftIdState.set(draftId);
    this.savedAtState.set(savedAt);
  }

  clear(): void {
    this.onboardingDataState.set(INITIAL_DATA);
    this.onboardingDraftState.set(INITIAL_DRAFT_STATE);
    this.currentStepIndexState.set(0);
    this.completedStepIndexesState.set([]);
    this.savedDraftIdState.set(null);
    this.savedAtState.set(null);
  }

  createStepDefinitions(): readonly OnboardingStepDefinition[] {
    return [
      { key: 'basics', label: 'Basics' },
      { key: 'localization', label: 'Localization' },
      { key: 'plan-limits', label: 'Plan & Limits' },
      { key: 'admin-user', label: 'Admin & User' },
    ];
  }
}