import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:intl/intl.dart';
import 'customer_model.dart';

class InvoiceItem {
  final String? id;
  final String? productId; // 追加
  String description;
  int quantity;
  int unitPrice;

  InvoiceItem({
    this.id,
    this.productId, // 追加
    required this.description,
    required this.quantity,
    required this.unitPrice,
  });

  int get subtotal => quantity * unitPrice;

  Map<String, dynamic> toMap(String invoiceId) {
    return {
      'id': id ?? DateTime.now().microsecondsSinceEpoch.toString(),
      'invoice_id': invoiceId,
      'product_id': productId, // 追加
      'description': description,
      'quantity': quantity,
      'unit_price': unitPrice,
    };
  }

  factory InvoiceItem.fromMap(Map<String, dynamic> map) {
    return InvoiceItem(
      id: map['id'],
      productId: map['product_id'], // 追加
      description: map['description'],
      quantity: map['quantity'],
      unitPrice: map['unit_price'],
    );
  }

  InvoiceItem copyWith({
    String? id, // Added this to be complete
    String? description,
    int? quantity,
    int? unitPrice,
    String? productId,
  }) {
    return InvoiceItem(
      id: id ?? this.id, // Added this to be complete
      description: description ?? this.description,
      quantity: quantity ?? this.quantity,
      unitPrice: unitPrice ?? this.unitPrice,
      productId: productId ?? this.productId,
    );
  }
}

enum DocumentType {
  estimation, // 見積
  delivery,   // 納品
  invoice,    // 請求
  receipt,    // 領収
}

extension DocumentTypeDisplay on DocumentType {
  String get displayName {
    switch (this) {
      case DocumentType.estimation:
        return '見積書';
      case DocumentType.delivery:
        return '納品書';
      case DocumentType.invoice:
        return '請求書';
      case DocumentType.receipt:
        return '領収書';
    }
  }
}

class Invoice {
  static const String lockStatement =
      '正式発行ボタン押下時にこの伝票はロックされ、以後の編集・削除はできません。ロック状態はハッシュチェーンで保護されます。';
  static const String hashDescription =
      'metaJson = JSON.stringify({id, invoiceNumber, customer, date, total, documentType, hash, lockStatement, companySnapshot, companySealHash}); metaHash = SHA-256(metaJson).';
  final String id;
  final Customer customer;
  final DateTime date;
  final List<InvoiceItem> items;
  final String? notes;
  final String? filePath;
  final double taxRate;
  final DocumentType documentType; // 追加
  final String? customerFormalNameSnapshot;
  final String? odooId;
  final bool isSynced;
  final DateTime updatedAt;
  final double? latitude; // 追加
  final double? longitude; // 追加
  final String terminalId; // 追加: 端末識別子
  final bool isDraft; // 追加: 下書きフラグ
  final String? subject; // 追加: 案件名
  final bool isLocked; // 追加: ロック
  final int? contactVersionId; // 追加: 連絡先バージョン
  final String? contactEmailSnapshot;
  final String? contactTelSnapshot;
  final String? contactAddressSnapshot;
  final String? companySnapshot; // 追加: 発行時会社情報スナップショット
  final String? companySealHash; // 追加: 角印画像ハッシュ
  final String? metaJson;
  final String? metaHash;
  final String? previousChainHash;
  final String? chainHash;
  final String chainStatus;

  Invoice({
    String? id,
    required this.customer,
    required this.date,
    required this.items,
    this.notes,
    this.filePath,
    this.taxRate = 0.10,
    this.documentType = DocumentType.invoice, // デフォルト請求書
    this.customerFormalNameSnapshot,
    this.odooId,
    this.isSynced = false,
    DateTime? updatedAt,
    this.latitude, // 追加
    this.longitude, // 追加
    String? terminalId, // 追加
    this.isDraft = false, // 追加: デフォルトは通常
    this.subject, // 追加: 案件
    this.isLocked = false,
    this.contactVersionId,
    this.contactEmailSnapshot,
    this.contactTelSnapshot,
    this.contactAddressSnapshot,
    this.companySnapshot,
    this.companySealHash,
    this.metaJson,
    this.metaHash,
    this.previousChainHash,
    this.chainHash,
    this.chainStatus = 'pending',
  })  : id = id ?? DateTime.now().millisecondsSinceEpoch.toString(),
        terminalId = terminalId ?? "T1", // デフォルト端末ID
        updatedAt = updatedAt ?? DateTime.now();

  /// 伝票内容から決定論的なハッシュを生成する (SHA256の一部)
  String get contentHash {
    final input = "$id|$terminalId|${date.toIso8601String()}|${customer.id}|$totalAmount|${subject ?? ""}|${items.map((e) => "${e.description}${e.quantity}${e.unitPrice}").join()}";
    final bytes = utf8.encode(input);
    return sha256.convert(bytes).toString().substring(0, 8).toUpperCase();
  }

  String get documentTypeName {
    switch (documentType) {
      case DocumentType.estimation: return "見積書";
      case DocumentType.delivery: return "納品書";
      case DocumentType.invoice: return "請求書";
      case DocumentType.receipt: return "領収書";
    }
  }

  static const Map<DocumentType, String> _docTypeShortLabel = {
    DocumentType.estimation: '見積',
    DocumentType.delivery: '納品',
    DocumentType.invoice: '請求',
    DocumentType.receipt: '領収',
  };

  String get invoiceNumberPrefix {
    switch (documentType) {
      case DocumentType.estimation: return "EST";
      case DocumentType.delivery: return "DEL";
      case DocumentType.invoice: return "INV";
      case DocumentType.receipt: return "REC";
    }
  }

  String get invoiceNumber => "$invoiceNumberPrefix-$terminalId-${DateFormat('yyyyMMdd').format(date)}-${id.substring(id.length > 4 ? id.length - 4 : 0)}";

  // 表示用の宛名（スナップショットがあれば優先）。必ず敬称を付与。
  String get customerNameForDisplay {
    final base = customerFormalNameSnapshot ?? customer.formalName;
    final hasHonorific = RegExp(r'(様|御中|殿)$').hasMatch(base);
    return hasHonorific ? base : '$base ${customer.title}';
  }

  int get subtotal => items.fold(0, (sum, item) => sum + item.subtotal);
  int get tax => (subtotal * taxRate).floor();
  int get totalAmount => subtotal + tax;

  String get _projectLabel {
    if (subject != null && subject!.trim().isNotEmpty) {
      return subject!.trim();
    }
    return '案件';
  }

  String get mailTitleCore {
    final dateStr = DateFormat('yyyyMMdd').format(date);
    final docLabel = _docTypeShortLabel[documentType] ?? documentTypeName.replaceAll('書', '');
    final customerCompact = customerNameForDisplay.replaceAll(RegExp(r'\s+'), '');
    final amountStr = NumberFormat('#,###').format(totalAmount);
    final buffer = StringBuffer()
      ..write(dateStr)
      ..write('($docLabel)')
      ..write(_projectLabel)
      ..write('@')
      ..write(customerCompact)
      ..write('_')
      ..write(amountStr)
      ..write('円');
    final raw = buffer.toString();
    return _sanitizeForFile(raw);
  }

  String get mailAttachmentFileName => '$mailTitleCore.PDF';

  String get mailBodyText => '請求書をお送りします。ご確認ください。';

  static String _sanitizeForFile(String input) {
    var sanitized = input.replaceAll(RegExp(r'[\\/:*?"<>|]'), '-');
    sanitized = sanitized.replaceAll(RegExp(r'[\r\n]+'), '');
    sanitized = sanitized.replaceAll('　', '');
    sanitized = sanitized.replaceAll(' ', '');
    return sanitized;
  }

  Map<String, dynamic> metaPayload() {
    return {
      'id': id,
      'invoiceNumber': invoiceNumber,
      'customer': customerNameForDisplay,
      'date': date.toIso8601String(),
      'total': totalAmount,
      'documentType': documentType.name,
      'hash': contentHash,
      'lockStatement': lockStatement,
      'hashDescription': hashDescription,
      'companySnapshot': companySnapshot,
      'companySealHash': companySealHash,
    };
  }

  String get metaJsonValue => metaJson ?? jsonEncode(metaPayload());

  String get metaHashValue => metaHash ?? sha256.convert(utf8.encode(metaJsonValue)).toString();

  Map<String, dynamic> toMap() {
    return {
      'id': id,
      'customer_id': customer.id,
      'date': date.toIso8601String(),
      'notes': notes,
      'file_path': filePath,
      'total_amount': totalAmount,
      'tax_rate': taxRate,
      'document_type': documentType.name, // 追加
      'customer_formal_name': customerFormalNameSnapshot ?? customer.formalName,
      'odoo_id': odooId,
      'is_synced': isSynced ? 1 : 0,
      'updated_at': updatedAt.toIso8601String(),
      'latitude': latitude, // 追加
      'longitude': longitude, // 追加
      'terminal_id': terminalId, // 追加
      'content_hash': contentHash, // 追加
      'is_draft': isDraft ? 1 : 0, // 追加
      'subject': subject, // 追加
      'is_locked': isLocked ? 1 : 0,
      'contact_version_id': contactVersionId,
      'contact_email_snapshot': contactEmailSnapshot,
      'contact_tel_snapshot': contactTelSnapshot,
      'contact_address_snapshot': contactAddressSnapshot,
      'company_snapshot': companySnapshot,
      'company_seal_hash': companySealHash,
      'meta_json': metaJsonValue,
      'meta_hash': metaHashValue,
      'previous_chain_hash': previousChainHash,
      'chain_hash': chainHash,
      'chain_status': chainStatus,
    };
  }

  Invoice copyWith({
    String? id,
    Customer? customer,
    DateTime? date,
    List<InvoiceItem>? items,
    String? notes,
    String? filePath,
    double? taxRate,
    DocumentType? documentType,
    String? customerFormalNameSnapshot,
    String? odooId,
    bool? isSynced,
    DateTime? updatedAt,
    double? latitude,
    double? longitude,
    String? terminalId,
    bool? isDraft,
    String? subject,
    bool? isLocked,
    int? contactVersionId,
    String? contactEmailSnapshot,
    String? contactTelSnapshot,
    String? contactAddressSnapshot,
    String? companySnapshot,
    String? companySealHash,
    String? metaJson,
    String? metaHash,
    String? previousChainHash,
    String? chainHash,
    String? chainStatus,
  }) {
    return Invoice(
      id: id ?? this.id,
      customer: customer ?? this.customer,
      date: date ?? this.date,
      items: items ?? List.from(this.items),
      notes: notes ?? this.notes,
      filePath: filePath ?? this.filePath,
      taxRate: taxRate ?? this.taxRate,
      documentType: documentType ?? this.documentType,
      customerFormalNameSnapshot: customerFormalNameSnapshot ?? this.customerFormalNameSnapshot,
      odooId: odooId ?? this.odooId,
      isSynced: isSynced ?? this.isSynced,
      updatedAt: updatedAt ?? this.updatedAt,
      latitude: latitude ?? this.latitude,
      longitude: longitude ?? this.longitude,
      terminalId: terminalId ?? this.terminalId,
      isDraft: isDraft ?? this.isDraft,
      subject: subject ?? this.subject,
      isLocked: isLocked ?? this.isLocked,
      contactVersionId: contactVersionId ?? this.contactVersionId,
      contactEmailSnapshot: contactEmailSnapshot ?? this.contactEmailSnapshot,
      contactTelSnapshot: contactTelSnapshot ?? this.contactTelSnapshot,
      contactAddressSnapshot: contactAddressSnapshot ?? this.contactAddressSnapshot,
      companySnapshot: companySnapshot ?? this.companySnapshot,
      companySealHash: companySealHash ?? this.companySealHash,
      metaJson: metaJson ?? this.metaJson,
      metaHash: metaHash ?? this.metaHash,
      previousChainHash: previousChainHash ?? this.previousChainHash,
      chainHash: chainHash ?? this.chainHash,
      chainStatus: chainStatus ?? this.chainStatus,
    );
  }

  String toCsv() {
    final buffer = StringBuffer();
    buffer.writeln('伝票種別,伝票番号,日付,取引先,合計金額,緯度,経度');
    buffer.writeln('$documentTypeName,$invoiceNumber,${DateFormat('yyyy/MM/dd').format(date)},$customerNameForDisplay,$totalAmount,${latitude ?? ""},${longitude ?? ""}');
    buffer.writeln('');
    buffer.writeln('品名,数量,単価,小計');
    for (var item in items) {
      buffer.writeln('${item.description},${item.quantity},${item.unitPrice},${item.subtotal}');
    }
    return buffer.toString();
  }
}
