import 'package:meta/meta.dart';

import '../models/invoice_models.dart';

const _unset = Object();

enum SalesEntryStatus { draft, confirmed, settled }

enum SettlementMethod { cash, bankTransfer, card, accountsReceivable, other }

extension SettlementMethodX on SettlementMethod {
  String get displayName {
    switch (this) {
      case SettlementMethod.cash:
        return '現金';
      case SettlementMethod.bankTransfer:
        return '銀行振込';
      case SettlementMethod.card:
        return 'カード';
      case SettlementMethod.accountsReceivable:
        return '売掛';
      case SettlementMethod.other:
        return 'その他';
    }
  }
}

SettlementMethod? settlementMethodFromName(String? value) {
  if (value == null) return null;
  for (final method in SettlementMethod.values) {
    if (method.name == value) {
      return method;
    }
  }
  return null;
}

@immutable
class SalesInvoiceImportData {
  const SalesInvoiceImportData({
    required this.invoiceId,
    required this.documentType,
    required this.issueDate,
    required this.taxRate,
    required this.totalAmount,
    required this.items,
    required this.isLocked,
    required this.chainStatus,
    required this.contentHash,
    this.customerId,
    this.customerFormalName,
    this.subject,
  });

  final String invoiceId;
  final DocumentType documentType;
  final DateTime issueDate;
  final double taxRate;
  final int totalAmount;
  final List<InvoiceItem> items;
  final bool isLocked;
  final String chainStatus;
  final String contentHash;
  final String? customerId;
  final String? customerFormalName;
  final String? subject;
}

extension SalesEntryStatusDisplay on SalesEntryStatus {
  String get displayName {
    switch (this) {
      case SalesEntryStatus.draft:
        return '下書き';
      case SalesEntryStatus.confirmed:
        return '確定';
      case SalesEntryStatus.settled:
        return '入金済み';
    }
  }
}

class SalesReceiptAllocationInput {
  const SalesReceiptAllocationInput({required this.salesEntryId, required this.amount});

  final String salesEntryId;
  final int amount;
}

@immutable
class SalesLineItem {
  const SalesLineItem({
    required this.id,
    required this.salesEntryId,
    required this.description,
    required this.quantity,
    required this.unitPrice,
    required this.lineTotal,
    this.productId,
    this.taxRate = 0,
    this.sourceInvoiceId,
    this.sourceInvoiceItemId,
    this.costAmount = 0,
    this.costIsProvisional = false,
  });

  final String id;
  final String salesEntryId;
  final String description;
  final int quantity;
  final int unitPrice;
  final int lineTotal;
  final String? productId;
  final double taxRate;
  final String? sourceInvoiceId;
  final String? sourceInvoiceItemId;
  final int costAmount;
  final bool costIsProvisional;

  Map<String, dynamic> toMap() => {
        'id': id,
        'sales_entry_id': salesEntryId,
        'product_id': productId,
        'description': description,
        'quantity': quantity,
        'unit_price': unitPrice,
        'tax_rate': taxRate,
        'line_total': lineTotal,
        'cost_amount': costAmount,
        'cost_is_provisional': costIsProvisional ? 1 : 0,
        'source_invoice_id': sourceInvoiceId,
        'source_invoice_item_id': sourceInvoiceItemId,
      };

  factory SalesLineItem.fromMap(Map<String, dynamic> map) => SalesLineItem(
        id: map['id'] as String,
        salesEntryId: map['sales_entry_id'] as String,
        productId: map['product_id'] as String?,
        description: map['description'] as String,
        quantity: map['quantity'] as int? ?? 0,
        unitPrice: map['unit_price'] as int? ?? 0,
        taxRate: (map['tax_rate'] as num?)?.toDouble() ?? 0,
        lineTotal: map['line_total'] as int? ?? 0,
        costAmount: map['cost_amount'] as int? ?? 0,
        costIsProvisional: (map['cost_is_provisional'] as int? ?? 0) == 1,
        sourceInvoiceId: map['source_invoice_id'] as String?,
        sourceInvoiceItemId: map['source_invoice_item_id'] as String?,
      );
  SalesLineItem copyWith({
    String? id,
    String? salesEntryId,
    String? description,
    int? quantity,
    int? unitPrice,
    int? lineTotal,
    String? productId,
    double? taxRate,
    String? sourceInvoiceId,
    String? sourceInvoiceItemId,
    int? costAmount,
    bool? costIsProvisional,
  }) {
    return SalesLineItem(
      id: id ?? this.id,
      salesEntryId: salesEntryId ?? this.salesEntryId,
      description: description ?? this.description,
      quantity: quantity ?? this.quantity,
      unitPrice: unitPrice ?? this.unitPrice,
      lineTotal: lineTotal ?? this.lineTotal,
      productId: productId ?? this.productId,
      taxRate: taxRate ?? this.taxRate,
      sourceInvoiceId: sourceInvoiceId ?? this.sourceInvoiceId,
      sourceInvoiceItemId: sourceInvoiceItemId ?? this.sourceInvoiceItemId,
      costAmount: costAmount ?? this.costAmount,
      costIsProvisional: costIsProvisional ?? this.costIsProvisional,
    );
  }
}

@immutable
class SalesEntry {
  const SalesEntry({
    required this.id,
    required this.issueDate,
    required this.status,
    required this.createdAt,
    required this.updatedAt,
    this.customerId,
    this.customerNameSnapshot,
    this.subject,
    this.amountTaxExcl = 0,
    this.taxAmount = 0,
    this.amountTaxIncl = 0,
    this.notes,
    this.settlementMethod,
    this.settlementCardCompany,
    this.settlementDueDate,
    this.items = const [],
  });

  final String id;
  final String? customerId;
  final String? customerNameSnapshot;
  final String? subject;
  final DateTime issueDate;
  final SalesEntryStatus status;
  final int amountTaxExcl;
  final int taxAmount;
  final int amountTaxIncl;
  final String? notes;
  final SettlementMethod? settlementMethod;
  final String? settlementCardCompany;
  final DateTime? settlementDueDate;
  final DateTime createdAt;
  final DateTime updatedAt;
  final List<SalesLineItem> items;

  SalesEntry copyWith({
    String? id,
    String? customerId,
    String? customerNameSnapshot,
    String? subject,
    DateTime? issueDate,
    SalesEntryStatus? status,
    int? amountTaxExcl,
    int? taxAmount,
    int? amountTaxIncl,
    String? notes,
    DateTime? createdAt,
    DateTime? updatedAt,
    List<SalesLineItem>? items,
    Object? settlementMethod = _unset,
    Object? settlementCardCompany = _unset,
    Object? settlementDueDate = _unset,
  }) {
    return SalesEntry(
      id: id ?? this.id,
      customerId: customerId ?? this.customerId,
      customerNameSnapshot: customerNameSnapshot ?? this.customerNameSnapshot,
      subject: subject ?? this.subject,
      issueDate: issueDate ?? this.issueDate,
      status: status ?? this.status,
      amountTaxExcl: amountTaxExcl ?? this.amountTaxExcl,
      taxAmount: taxAmount ?? this.taxAmount,
      amountTaxIncl: amountTaxIncl ?? this.amountTaxIncl,
      notes: notes ?? this.notes,
      settlementMethod: settlementMethod == _unset ? this.settlementMethod : settlementMethod as SettlementMethod?,
      settlementCardCompany: settlementCardCompany == _unset ? this.settlementCardCompany : settlementCardCompany as String?,
      settlementDueDate: settlementDueDate == _unset ? this.settlementDueDate : settlementDueDate as DateTime?,
      createdAt: createdAt ?? this.createdAt,
      updatedAt: updatedAt ?? this.updatedAt,
      items: items ?? this.items,
    );
  }

  Map<String, dynamic> toMap() => {
        'id': id,
        'customer_id': customerId,
        'customer_name_snapshot': customerNameSnapshot,
        'subject': subject,
        'issue_date': issueDate.toIso8601String(),
        'status': status.name,
        'amount_tax_excl': amountTaxExcl,
        'tax_amount': taxAmount,
        'amount_tax_incl': amountTaxIncl,
        'notes': notes,
        'settlement_method': settlementMethod?.name,
        'settlement_card_company': settlementCardCompany,
        'settlement_due_date': settlementDueDate?.toIso8601String(),
        'created_at': createdAt.toIso8601String(),
        'updated_at': updatedAt.toIso8601String(),
      };

  factory SalesEntry.fromMap(Map<String, dynamic> map, {List<SalesLineItem> items = const []}) => SalesEntry(
        id: map['id'] as String,
        customerId: map['customer_id'] as String?,
        customerNameSnapshot: map['customer_name_snapshot'] as String?,
        subject: map['subject'] as String?,
        issueDate: DateTime.parse(map['issue_date'] as String),
        status: SalesEntryStatus.values.firstWhere(
          (s) => s.name == map['status'],
          orElse: () => SalesEntryStatus.draft,
        ),
        amountTaxExcl: map['amount_tax_excl'] as int? ?? 0,
        taxAmount: map['tax_amount'] as int? ?? 0,
        amountTaxIncl: map['amount_tax_incl'] as int? ?? 0,
        notes: map['notes'] as String?,
        settlementMethod: settlementMethodFromName(map['settlement_method'] as String?),
        settlementCardCompany: map['settlement_card_company'] as String?,
        settlementDueDate: map['settlement_due_date'] != null && (map['settlement_due_date'] as String).isNotEmpty
            ? DateTime.parse(map['settlement_due_date'] as String)
            : null,
        createdAt: DateTime.parse(map['created_at'] as String),
        updatedAt: DateTime.parse(map['updated_at'] as String),
        items: items,
      );

  SalesEntry recalcTotals() {
    final subtotal = items.fold<int>(0, (sum, item) => sum + item.lineTotal);
    final tax = items.fold<double>(0, (sum, item) => sum + item.lineTotal * item.taxRate).round();
    return copyWith(
      amountTaxExcl: subtotal,
      taxAmount: tax,
      amountTaxIncl: subtotal + tax,
    );
  }
}

@immutable
class SalesReceipt {
  const SalesReceipt({
    required this.id,
    required this.paymentDate,
    required this.amount,
    required this.createdAt,
    required this.updatedAt,
    this.customerId,
    this.method,
    this.notes,
  });

  final String id;
  final String? customerId;
  final DateTime paymentDate;
  final String? method;
  final int amount;
  final String? notes;
  final DateTime createdAt;
  final DateTime updatedAt;

  Map<String, dynamic> toMap() => {
        'id': id,
        'customer_id': customerId,
        'payment_date': paymentDate.toIso8601String(),
        'method': method,
        'amount': amount,
        'notes': notes,
        'created_at': createdAt.toIso8601String(),
        'updated_at': updatedAt.toIso8601String(),
      };

  factory SalesReceipt.fromMap(Map<String, dynamic> map) => SalesReceipt(
        id: map['id'] as String,
        customerId: map['customer_id'] as String?,
        paymentDate: DateTime.parse(map['payment_date'] as String),
        method: map['method'] as String?,
        amount: map['amount'] as int? ?? 0,
        notes: map['notes'] as String?,
        createdAt: DateTime.parse(map['created_at'] as String),
        updatedAt: DateTime.parse(map['updated_at'] as String),
      );

  SalesReceipt copyWith({
    String? id,
    String? customerId,
    DateTime? paymentDate,
    String? method,
    int? amount,
    String? notes,
    DateTime? createdAt,
    DateTime? updatedAt,
  }) {
    return SalesReceipt(
      id: id ?? this.id,
      customerId: customerId ?? this.customerId,
      paymentDate: paymentDate ?? this.paymentDate,
      method: method ?? this.method,
      amount: amount ?? this.amount,
      notes: notes ?? this.notes,
      createdAt: createdAt ?? this.createdAt,
      updatedAt: updatedAt ?? this.updatedAt,
    );
  }
}

@immutable
class SalesReceiptLink {
  const SalesReceiptLink({required this.receiptId, required this.salesEntryId, required this.allocatedAmount});

  final String receiptId;
  final String salesEntryId;
  final int allocatedAmount;

  Map<String, dynamic> toMap() => {
        'receipt_id': receiptId,
        'sales_entry_id': salesEntryId,
        'allocated_amount': allocatedAmount,
      };

  factory SalesReceiptLink.fromMap(Map<String, dynamic> map) => SalesReceiptLink(
        receiptId: map['receipt_id'] as String,
        salesEntryId: map['sales_entry_id'] as String,
        allocatedAmount: map['allocated_amount'] as int? ?? 0,
      );
}

@immutable
class SalesEntrySource {
  const SalesEntrySource({
    required this.id,
    required this.salesEntryId,
    required this.invoiceId,
    required this.importedAt,
    this.invoiceHashSnapshot,
  });

  final String id;
  final String salesEntryId;
  final String invoiceId;
  final DateTime importedAt;
  final String? invoiceHashSnapshot;

  Map<String, dynamic> toMap() => {
        'id': id,
        'sales_entry_id': salesEntryId,
        'invoice_id': invoiceId,
        'imported_at': importedAt.toIso8601String(),
        'invoice_hash_snapshot': invoiceHashSnapshot,
      };

  factory SalesEntrySource.fromMap(Map<String, dynamic> map) => SalesEntrySource(
        id: map['id'] as String,
        salesEntryId: map['sales_entry_id'] as String,
        invoiceId: map['invoice_id'] as String,
        importedAt: DateTime.parse(map['imported_at'] as String),
        invoiceHashSnapshot: map['invoice_hash_snapshot'] as String?,
      );
}

@immutable
class SalesImportCandidate {
  const SalesImportCandidate({
    required this.invoiceId,
    required this.invoiceNumber,
    required this.documentType,
    required this.invoiceDate,
    required this.customerName,
    required this.totalAmount,
    required this.isLocked,
    required this.chainStatus,
    required this.contentHash,
    this.subject,
  });

  final String invoiceId;
  final String invoiceNumber;
  final DocumentType documentType;
  final DateTime invoiceDate;
  final String customerName;
  final int totalAmount;
  final bool isLocked;
  final String chainStatus;
  final String contentHash;
  final String? subject;

  String get documentTypeName => documentType.displayName;

  factory SalesImportCandidate.fromMap(Map<String, Object?> row) {
    final docTypeName = row['document_type'] as String? ?? DocumentType.invoice.name;
    final documentType = DocumentType.values.firstWhere(
      (type) => type.name == docTypeName,
      orElse: () => DocumentType.invoice,
    );
    return SalesImportCandidate(
      invoiceId: row['id'] as String,
      invoiceNumber: row['invoice_number'] as String,
      documentType: documentType,
      invoiceDate: DateTime.parse(row['date'] as String),
      customerName: row['customer_name'] as String? ?? '-',
      totalAmount: (row['total_amount'] as num?)?.toInt() ?? 0,
      isLocked: (row['is_locked'] as int?) == 1,
      chainStatus: row['chain_status'] as String? ?? 'pending',
      contentHash: row['content_hash'] as String? ?? '',
      subject: row['subject'] as String?,
    );
  }
}
