import {
  ConflictException,
  Inject,
  Injectable,
  NotFoundException,
} from "@nestjs/common";

import { CreateDropdownOptionDto } from "./dto/create-dropdown-option.dto";
import { CreateDropdownSettingDto } from "./dto/create-dropdown-setting.dto";
import { UpdateDropdownOptionDto } from "./dto/update-dropdown-option.dto";
import { UpdateDropdownSettingDto } from "./dto/update-dropdown-setting.dto";
import { DropdownOption } from "./entities/dropdown-option.entity";
import { DropdownSetting } from "./entities/dropdown-setting.entity";
import {
  DROPDOWN_SETTINGS_REPOSITORY,
  IDropdownSettingsRepository,
} from "./interfaces/dropdown-settings.repository.interface";

const STATIONS_TER_CODE = "stations_ter";

const DEFAULT_STATION_OPTIONS: CreateDropdownOptionDto[] = [
  {
    value: "inside_addis_ababa",
    label: "Addis Ababa",
    note: "Inside country",
    order: 1,
  },
  {
    value: "inside_adama",
    label: "Adama",
    note: "Inside country",
    order: 2,
  },
  {
    value: "inside_mojo",
    label: "Mojo",
    note: "Inside country",
    order: 3,
  },
  {
    value: "inside_awash",
    label: "Awash",
    note: "Inside country",
    order: 4,
  },
  {
    value: "inside_mieso",
    label: "Mieso",
    note: "Inside country",
    order: 5,
  },
  {
    value: "inside_dire_dawa",
    label: "Dire Dawa",
    note: "Inside country",
    order: 6,
  },
  {
    value: "outside_ali_sabieh",
    label: "Ali Sabieh",
    note: "Outside country",
    order: 7,
  },
  {
    value: "outside_holhol",
    label: "Holhol",
    note: "Outside country",
    order: 8,
  },
  {
    value: "outside_djibouti_city",
    label: "Djibouti City",
    note: "Outside country",
    order: 9,
  },
  {
    value: "outside_doraleh_terminal",
    label: "Doraleh Terminal",
    note: "Outside country",
    order: 10,
  },
];

@Injectable()
export class DropdownSettingsService {
  constructor(
    @Inject(DROPDOWN_SETTINGS_REPOSITORY)
    private readonly repository: IDropdownSettingsRepository,
  ) {}

  list(): Promise<DropdownSetting[]> {
    return this.repository.findAll();
  }

  async getById(id: string): Promise<DropdownSetting> {
    const setting = await this.repository.findById(id);
    if (!setting) throw new NotFoundException(`Setting ${id} not found`);
    return setting;
  }

  async getByCode(code: string): Promise<DropdownSetting> {
    const setting = await this.repository.findByCode(code);
    if (!setting) throw new NotFoundException(`Setting "${code}" not found`);
    return setting;
  }

  async create(dto: CreateDropdownSettingDto): Promise<DropdownSetting> {
    const existing = await this.repository.findByCode(dto.code);
    if (existing) {
      throw new ConflictException(
        `Dropdown setting with code "${dto.code}" already exists`,
      );
    }

    const setting = await this.repository.create({
      code: dto.code,
      label: dto.label,
      description: dto.description ?? null,
      multiple: dto.multiple ?? false,
      meta: dto.meta ?? null,
    });

    if (dto.children && dto.children.length > 0) {
      await this.repository.replaceOptions(setting.id, dto.children);
    }

    return this.getById(setting.id);
  }

  async seedDefaultStations(): Promise<void> {
    const existing = await this.repository.findByCode(STATIONS_TER_CODE);

    if (!existing) {
      await this.create({
        code: STATIONS_TER_CODE,
        label: "Stations TER",
        description:
          "Temporary freight station list used by booking origin and destination yards.",
        multiple: false,
        meta: {
          searchable: true,
          clearable: true,
          version: "temporary",
        },
        children: DEFAULT_STATION_OPTIONS,
      });
      return;
    }

    if ((existing.children?.length ?? 0) === 0) {
      await this.repository.replaceOptions(
        existing.id,
        DEFAULT_STATION_OPTIONS,
      );
    }
  }

  async update(
    id: string,
    dto: UpdateDropdownSettingDto,
  ): Promise<DropdownSetting> {
    await this.getById(id);
    const updated = await this.repository.update(id, dto);
    if (!updated) throw new NotFoundException(`Setting ${id} not found`);
    return this.getById(id);
  }

  async remove(id: string): Promise<void> {
    await this.getById(id);
    await this.repository.softDelete(id);
  }

  /* ------------------------ option operations ------------------------ */

  async replaceOptions(
    settingId: string,
    options: CreateDropdownOptionDto[],
  ): Promise<DropdownOption[]> {
    await this.getById(settingId);
    return this.repository.replaceOptions(settingId, options);
  }

  async addOption(
    settingId: string,
    dto: CreateDropdownOptionDto,
  ): Promise<DropdownOption> {
    await this.getById(settingId);
    return this.repository.addOption(settingId, dto);
  }

  async updateOption(
    optionId: string,
    dto: UpdateDropdownOptionDto,
  ): Promise<DropdownOption> {
    const updated = await this.repository.updateOption(optionId, dto);
    if (!updated) throw new NotFoundException(`Option ${optionId} not found`);
    return updated;
  }

  async removeOption(optionId: string): Promise<void> {
    await this.repository.removeOption(optionId);
  }
}
