import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { uuidV7 } from '../common/uuid-v7';
import { AiForm, type AiFormField } from './entities/ai-form.entity';

export const A2UI_FIELD_TYPES = ['input', 'textarea', 'number', 'select', 'date'] as const;

const MAX_TITLE = 50;
const MAX_DESCRIPTION = 200;
const MAX_SUBMIT_LABEL = 20;
const MAX_FIELDS = 12;
const MAX_NAME = 50;
const MAX_LABEL = 50;
const MAX_PLACEHOLDER = 100;
const MAX_DEFAULT = 200;
const MAX_OPTIONS = 20;
const MAX_OPTION_TEXT = 50;
const MAX_VALUE_LENGTH = 200;
const MAX_VALUES_BYTES = 64 * 1024;

const FIELD_NAME_RE = /^[a-zA-Z0-9_]{1,50}$/;
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;

const FIELD_KEYS = new Set([
  'name',
  'label',
  'type',
  'required',
  'placeholder',
  'defaultValue',
  'options',
]);
const SCHEMA_KEYS = new Set(['title', 'description', 'submitLabel', 'fields']);

interface ValidatedFormSchema {
  title: string;
  description: string | null;
  submitLabel: string;
  fields: AiFormField[];
}

function isPlainRecord(value: unknown): value is Record<string, unknown> {
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}

function isShortString(value: unknown, max: number): value is string {
  return typeof value === 'string' && value.length <= max;
}

function requireString(
  value: unknown,
  label: string,
  max: number,
  optional = false,
): string {
  if (value === undefined || value === null) {
    if (optional) return '';
    throw new BadRequestException(`${label}不能为空`);
  }
  if (typeof value !== 'string' || !value.trim()) {
    throw new BadRequestException(`${label}必须是字符串`);
  }
  const trimmed = value.trim();
  if (trimmed.length > max) {
    throw new BadRequestException(`${label}长度不能超过 ${max}`);
  }
  return trimmed;
}

/**
 * Server-side A2UI form lifecycle:
 * schema validation + persistence, owned lookup, submitted-value
 * validation, and serialization for SSE / message metadata.
 */
@Injectable()
export class AiFormService {
  constructor(
    @InjectRepository(AiForm)
    private readonly forms: Repository<AiForm>,
  ) {}

  /**
   * Validate `render_form` arguments and persist a pending form.
   * Throws BadRequestException when the schema is unsafe/invalid.
   */
  async createForm(
    input: { userId: number; conversationId: number; assistantMessageId: number },
    rawArgs: unknown,
  ): Promise<AiForm> {
    const schema = this.validateSchema(rawArgs);
    return this.forms.save(
      this.forms.create({
        id: uuidV7(),
        userId: input.userId,
        conversationId: input.conversationId,
        assistantMessageId: input.assistantMessageId,
        title: schema.title,
        description: schema.description,
        submitLabel: schema.submitLabel,
        fieldsJson: JSON.stringify(schema.fields),
        status: 'pending',
        submittedValuesJson: null,
        submittedAt: null,
      }),
    );
  }

  async findOwnedPending(formId: string, userId: number): Promise<AiForm> {
    const form = await this.forms.findOne({ where: { id: formId, userId, status: 'pending' } });
    if (!form) throw new NotFoundException('表单不存在或已提交');
    return form;
  }

  async markSubmitted(form: AiForm, values: Record<string, unknown>): Promise<AiForm> {
    form.status = 'submitted';
    form.submittedValuesJson = JSON.stringify(values);
    form.submittedAt = new Date();
    return this.forms.save(form);
  }

  /**
   * Validate submitted values against the stored schema.
   * Returns a sanitized record containing only known field names.
   * Throws BadRequestException on invalid input.
   */
  validateValues(form: AiForm, rawValues: unknown): Record<string, unknown> {
    if (!isPlainRecord(rawValues)) throw new BadRequestException('提交内容格式无效');

    const fields = this.parseFields(form.fieldsJson);
    const known = new Set(fields.map((field) => field.name));
    for (const key of Object.keys(rawValues)) {
      if (!known.has(key)) throw new BadRequestException(`包含未知字段: ${key}`);
    }

    const result: Record<string, unknown> = {};
    for (const field of fields) {
      const value = rawValues[field.name];
      if (value === undefined || value === null || value === '') {
        if (field.required) throw new BadRequestException(`「${field.label}」为必填项`);
        continue;
      }
      result[field.name] = this.normalizeValue(field, value);
    }

    let serialized: string;
    try {
      serialized = JSON.stringify(result);
    } catch {
      throw new BadRequestException('提交内容无法序列化');
    }
    if (serialized.length > MAX_VALUES_BYTES) throw new BadRequestException('提交内容过长');
    return result;
  }

  /** Public shape sent via `ui.form` SSE and mirrored into message metadata. */
  serialize(form: AiForm): Record<string, unknown> {
    return {
      id: form.id,
      title: form.title,
      description: form.description,
      submitLabel: form.submitLabel,
      fields: this.parseFields(form.fieldsJson),
      status: form.status,
    };
  }

  parseFields(fieldsJson: string): AiFormField[] {
    try {
      const parsed: unknown = JSON.parse(fieldsJson);
      if (!Array.isArray(parsed)) return [];
      return parsed.filter((item): item is AiFormField => isPlainRecord(item));
    } catch {
      return [];
    }
  }

  private validateSchema(rawArgs: unknown): ValidatedFormSchema {
    if (!isPlainRecord(rawArgs)) throw new BadRequestException('表单参数必须是对象');
    for (const key of Object.keys(rawArgs)) {
      if (!SCHEMA_KEYS.has(key)) throw new BadRequestException(`表单包含未知字段: ${key}`);
    }

    const title = requireString(rawArgs.title, '表单标题', MAX_TITLE);
    const description = requireString(rawArgs.description, '表单说明', MAX_DESCRIPTION, true) || null;
    const submitLabel = requireString(rawArgs.submitLabel, '提交按钮文案', MAX_SUBMIT_LABEL, true);

    if (!Array.isArray(rawArgs.fields) || rawArgs.fields.length === 0) {
      throw new BadRequestException('表单至少需要一个字段');
    }
    if (rawArgs.fields.length > MAX_FIELDS) {
      throw new BadRequestException(`表单字段不能超过 ${MAX_FIELDS} 个`);
    }

    const seen = new Set<string>();
    const fields = rawArgs.fields.map((item, index) => this.validateField(item, index, seen));
    return {
      title,
      description,
      submitLabel: submitLabel || '提交',
      fields,
    };
  }

  private validateField(raw: unknown, index: number, seen: Set<string>): AiFormField {
    if (!isPlainRecord(raw)) throw new BadRequestException(`第 ${index + 1} 个字段格式无效`);
    for (const key of Object.keys(raw)) {
      if (!FIELD_KEYS.has(key)) throw new BadRequestException(`字段包含未知属性: ${key}`);
    }

    const name = requireString(raw.name, '字段名', MAX_NAME);
    if (!FIELD_NAME_RE.test(name)) {
      throw new BadRequestException(`字段名 ${name} 只能包含字母、数字、下划线`);
    }
    if (seen.has(name)) throw new BadRequestException(`字段名重复: ${name}`);
    seen.add(name);

    const label = requireString(raw.label, '字段标签', MAX_LABEL);
    const type = raw.type;
    if (typeof type !== 'string' || !(A2UI_FIELD_TYPES as readonly string[]).includes(type)) {
      throw new BadRequestException(`字段 ${name} 的类型不支持`);
    }
    const fieldType = type as AiFormField['type'];

    if (raw.required !== undefined && typeof raw.required !== 'boolean') {
      throw new BadRequestException(`字段 ${name} 的 required 必须是布尔值`);
    }
    const placeholder = requireString(raw.placeholder, `字段 ${name} 的 placeholder`, MAX_PLACEHOLDER, true);
    let defaultValue: string | number | undefined;
    if (raw.defaultValue !== undefined && raw.defaultValue !== null) {
      if (typeof raw.defaultValue === 'number') {
        if (!Number.isFinite(raw.defaultValue)) {
          throw new BadRequestException(`字段 ${name} 的 defaultValue 必须是有限数字`);
        }
        defaultValue = raw.defaultValue;
      } else if (isShortString(raw.defaultValue, MAX_DEFAULT)) {
        defaultValue = raw.defaultValue;
      } else {
        throw new BadRequestException(`字段 ${name} 的 defaultValue 无效`);
      }
    }

    let options: Array<{ label: string; value: string }> | undefined;
    if (fieldType === 'select') {
      if (!Array.isArray(raw.options) || raw.options.length === 0 || raw.options.length > MAX_OPTIONS) {
        throw new BadRequestException(`字段 ${name} 的 select 选项数量必须在 1 到 ${MAX_OPTIONS} 之间`);
      }
      options = raw.options.map((option, optionIndex) => {
        if (!isPlainRecord(option)) {
          throw new BadRequestException(`字段 ${name} 第 ${optionIndex + 1} 个选项格式无效`);
        }
        const optionLabel = requireString(option.label, `字段 ${name} 的选项标签`, MAX_OPTION_TEXT);
        const optionValue = requireString(option.value, `字段 ${name} 的选项值`, MAX_OPTION_TEXT);
        return { label: optionLabel, value: optionValue };
      });
    } else if (raw.options !== undefined) {
      throw new BadRequestException(`字段 ${name} 只有 select 类型可以带 options`);
    }

    return {
      name,
      label,
      type: fieldType,
      required: raw.required === true,
      placeholder: placeholder || undefined,
      defaultValue,
      options,
    };
  }

  private normalizeValue(field: AiFormField, value: unknown): unknown {
    if (field.type === 'number') {
      if (typeof value !== 'number' || !Number.isFinite(value)) {
        throw new BadRequestException(`「${field.label}」必须是数字`);
      }
      return value;
    }
    if (typeof value !== 'string' || value.length > MAX_VALUE_LENGTH) {
      throw new BadRequestException(`「${field.label}」格式无效`);
    }
    if (field.type === 'date' && !DATE_RE.test(value)) {
      throw new BadRequestException(`「${field.label}」必须是 YYYY-MM-DD 格式`);
    }
    if (field.type === 'select') {
      const valid = field.options?.some((option) => option.value === value);
      if (!valid) throw new BadRequestException(`「${field.label}」选项无效`);
    }
    return value;
  }
}
