import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { In, Repository } from 'typeorm';
import { AppSetting } from './app-setting.entity';

/**
 * Settings the mobile app reads on startup. Defaults live here so a fresh
 * install behaves sensibly before an administrator customises anything.
 */
export const PUBLIC_SETTING_KEYS = [
  'app.disclaimer',
  'app.support_email',
  'app.min_supported_version',
  'content.cache_ttl_minutes',
] as const;

const DEFAULTS: Record<string, any> = {
  'app.disclaimer':
    'This application is for education and training only. It does not provide ' +
    'medical advice and must not be used for clinical decision-making.',
  'content.cache_ttl_minutes': 60,
};

@Injectable()
export class AppSettingsService {
  constructor(
    @InjectRepository(AppSetting)
    private readonly repo: Repository<AppSetting>,
  ) {}

  async findAll(): Promise<Record<string, any>> {
    const rows = await this.repo.find({ order: { key: 'ASC' } });
    return this.toRecord(rows);
  }

  /** Only the whitelisted keys ever reach the unauthenticated mobile client. */
  async findPublic(): Promise<Record<string, any>> {
    const rows = await this.repo.find({ where: { key: In([...PUBLIC_SETTING_KEYS]) } });
    return { ...DEFAULTS, ...this.toRecord(rows) };
  }

  async get<T = any>(key: string, fallback?: T): Promise<T | undefined> {
    const row = await this.repo.findOne({ where: { key } });
    if (!row) return fallback ?? DEFAULTS[key];
    return (row.value as any)?.value ?? (row.value as any) ?? fallback;
  }

  async upsert(key: string, value: any): Promise<AppSetting> {
    const existing = await this.repo.findOne({ where: { key } });
    if (existing) {
      existing.value = this.wrap(value);
      return this.repo.save(existing);
    }
    return this.repo.save(this.repo.create({ key, value: this.wrap(value) }));
  }

  async upsertMany(values: Record<string, any>): Promise<Record<string, any>> {
    for (const [key, value] of Object.entries(values)) {
      await this.upsert(key, value);
    }
    return this.findAll();
  }

  async delete(key: string): Promise<boolean> {
    const result = await this.repo.delete({ key });
    return (result.affected || 0) > 0;
  }

  /** MySQL JSON columns cannot hold a bare scalar, so scalars are boxed. */
  private wrap(value: any): Record<string, any> {
    return value !== null && typeof value === 'object' && !Array.isArray(value)
      ? value
      : { value };
  }

  private toRecord(rows: AppSetting[]): Record<string, any> {
    const out: Record<string, any> = {};
    for (const row of rows) {
      const raw = row.value as any;
      out[row.key] =
        raw && typeof raw === 'object' && 'value' in raw && Object.keys(raw).length === 1
          ? raw.value
          : raw;
    }
    return out;
  }
}
