package templates

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
)

var (
	ErrInvalidReportCode = errors.New("invalid or unregistered report code")
	ErrEmptyTemplateData = errors.New("template data cannot be empty")
)

// Service coordinates report templates and definitions.
type Service struct {
	repo        *Repository
	definitions map[string]ReportDefinition
}

// NewService creates a new Service.
func NewService(repo *Repository) *Service {
	s := &Service{
		repo:        repo,
		definitions: make(map[string]ReportDefinition),
	}
	s.registerDefaultDefinitions()
	return s
}

func (s *Service) registerDefaultDefinitions() {
	s.definitions["invoice-print"] = ReportDefinition{
		Code:        "invoice-print",
		Name:        "Faktur Penjualan (Invoice)",
		Module:      "sales",
		Description: "Faktur penjualan resmi untuk pelanggan atau pembeli B2B",
		Parameters: []ReportParameter{
			{Name: "invoice_id", Label: "ID Faktur", Type: "integer", Required: true, Description: "ID faktur penjualan"},
		},
		SampleData: json.RawMessage(`{
			"company": {
				"name": "KeluargaMart Distributor",
				"address": "Jl. Sentral Distribusi No. 88, Jakarta Barat",
				"phone": "021-5558901",
				"logo_url": ""
			},
			"invoice": {
				"id": 1,
				"number": "INV-2026-0001",
				"date": "13/09/2026",
				"due_date": "27/09/2026",
				"status": "confirmed",
				"so_number": "SO-202609-0001"
			},
			"customer": {
				"id": 101,
				"name": "PT Berkah Jaya Abadi",
				"address": "Jl. Sudirman No. 45, Bandung",
				"phone": "081234567890"
			},
			"items": [
				{ "product_code": "BRS-01", "product_name": "Beras Premium Ramos 5kg", "quantity": 10, "unit": "Sak", "unit_price": 75000, "discount": 0, "subtotal": 750000 },
				{ "product_code": "MYK-02", "product_name": "Minyak Goreng Sania 2L", "quantity": 24, "unit": "Pouch", "unit_price": 36000, "discount": 0, "subtotal": 864000 },
				{ "product_code": "GLA-03", "product_name": "Gula Pasir Gulaku 1kg", "quantity": 20, "unit": "Kg", "unit_price": 18000, "discount": 0, "subtotal": 360000 }
			],
			"totals": {
				"subtotal": 1974000,
				"discount_amount": 0,
				"tax_amount": 217140,
				"grand_total": 2191140,
				"paid_amount": 0,
				"remaining_amount": 2191140
			},
			"meta": {
				"notes": "Pembayaran ditransfer ke rekening BCA 77889900 a/n PT Keluarga Mart Nusantara",
				"printed_by": "Administrator",
				"printed_at": "13/09/2026 22:30"
			}
		}`),
	}

	s.definitions["po-print"] = ReportDefinition{
		Code:        "po-print",
		Name:        "Purchase Order (PO)",
		Module:      "purchasing",
		Description: "Dokumen resmi pesanan pembelian kepada supplier",
		Parameters: []ReportParameter{
			{Name: "id", Label: "ID Purchase Order", Type: "integer", Required: true},
		},
		SampleData: json.RawMessage(`{
			"company": {
				"name": "KeluargaMart Distributor",
				"address": "Jl. Sentral Distribusi No. 88, Jakarta Barat",
				"phone": "021-5558901"
			},
			"po": {
				"id": 1,
				"number": "PO-2026-0001",
				"order_date": "13/09/2026",
				"expected_date": "20/09/2026",
				"status": "approved"
			},
			"supplier": {
				"name": "PT Pangan Nusantara Raya",
				"address": "Kawasan Industri MM2100 Blok B-12, Cikarang",
				"phone": "021-8980123"
			},
			"warehouse": {
				"name": "Gudang Pusat Cikokol"
			},
			"items": [
				{ "product_code": "BRS-01", "product_name": "Beras Premium Ramos 5kg", "quantity": 100, "unit": "Sak", "unit_price": 68000, "subtotal": 6800000 },
				{ "product_code": "MYK-02", "product_name": "Minyak Goreng Sania 2L", "quantity": 120, "unit": "Pouch", "unit_price": 32000, "subtotal": 3840000 }
			],
			"totals": {
				"subtotal": 10640000,
				"tax_amount": 1170400,
				"discount_amount": 0,
				"grand_total": 11810400
			},
			"meta": {
				"notes": "Pengiriman barang wajib menyertakan surat jalan dan sertifikat uji kualitas",
				"created_by": "Staff Purchasing",
				"approved_by": "Manager Logistik"
			}
		}`),
	}

	s.definitions["do-print"] = ReportDefinition{
		Code:        "do-print",
		Name:        "Surat Jalan / Delivery Order",
		Module:      "distribution",
		Description: "Dokumen surat jalan resmi ekspedisi pengiriman barang ke customer/outlet",
		Parameters: []ReportParameter{
			{Name: "id", Label: "ID Delivery Order", Type: "integer", Required: true},
		},
		SampleData: json.RawMessage(`{
			"company": {
				"name": "KeluargaMart Distributor",
				"address": "Jl. Sentral Distribusi No. 88, Jakarta Barat",
				"phone": "021-5558901"
			},
			"delivery_order": {
				"id": 1,
				"number": "DO-202609-0001",
				"so_number": "SO-202609-0088",
				"date": "13/09/2026",
				"status": "in_transit"
			},
			"customer": {
				"name": "Toko Barokah Sejahtera",
				"shipping_address": "Jl. Cibadak No. 120, Sukasari, Bandung",
				"recipient_name": "Bpk. H. Rahmat"
			},
			"warehouse": {
				"name": "Gudang Distribusi Bandung"
			},
			"items": [
				{ "product_code": "BRS-01", "product_name": "Beras Premium Ramos 5kg", "quantity_requested": 50, "quantity_shipped": 50, "unit": "Sak", "unit_price": 68000, "subtotal": 3400000 },
				{ "product_code": "MYK-02", "product_name": "Minyak Goreng Sania 2L", "quantity_requested": 48, "quantity_shipped": 48, "unit": "Pouch", "unit_price": 32000, "subtotal": 1536000 },
				{ "product_code": "GLA-03", "product_name": "Gula Pasir Gulaku 1kg", "quantity_requested": 30, "quantity_shipped": 30, "unit": "Kg", "unit_price": 17500, "subtotal": 525000 }
			],
			"totals": {
				"subtotal": 5461000,
				"grand_total": 5461000
			},
			"meta": {
				"notes": "Barang telah diperiksa dalam kondisi baik dan segel utuh sebelum dikirim",
				"driver_name": "Budi Santoso (B 9876 KM)",
				"dispatcher": "Staff Logistik Gudang"
			}
		}`),
	}

	s.definitions["receipt-print"] = ReportDefinition{
		Code:        "receipt-print",
		Name:        "Bukti Penerimaan Barang (LPB)",
		Module:      "purchasing",
		Description: "Laporan Penerimaan Barang (LPB) fisik di gudang dari supplier",
		Parameters: []ReportParameter{
			{Name: "id", Label: "ID Penerimaan Barang", Type: "integer", Required: true},
		},
		SampleData: json.RawMessage(`{
			"company": {
				"name": "KeluargaMart Distributor",
				"address": "Jl. Sentral Distribusi No. 88, Jakarta Barat",
				"phone": "021-5558901"
			},
			"receipt": {
				"id": 1,
				"number": "GR-202609-0001",
				"po_number": "PO-2026-0001",
				"received_date": "13/09/2026",
				"supplier_name": "PT Pangan Nusantara Raya",
				"warehouse_name": "Gudang Pusat Cikokol",
				"received_by": "Ahmad Fauzi"
			},
			"items": [
				{ "product_code": "BRS-01", "product_name": "Beras Premium Ramos 5kg", "quantity_ordered": 100, "quantity_received": 100, "batch_number": "BCH-2609-01" },
				{ "product_code": "MYK-02", "product_name": "Minyak Goreng Sania 2L", "quantity_ordered": 120, "quantity_received": 120, "batch_number": "BCH-2609-02" }
			],
			"meta": {
				"notes": "Fisik barang diperiksa dalam kondisi baik, tanggal kedaluwarsa minimal 12 bulan ke depan",
				"received_by": "Ahmad Fauzi"
			}
		}`),
	}

	s.definitions["sales-by-product"] = ReportDefinition{
		Code:        "sales-by-product",
		Name:        "Laporan Penjualan per Produk",
		Module:      "sales",
		Description: "Laporan analitik penjualan dikelompokkan per item produk",
		Parameters: []ReportParameter{
			{Name: "date_from", Label: "Tanggal Mulai", Type: "date", Required: false},
			{Name: "date_to", Label: "Tanggal Selesai", Type: "date", Required: false},
		},
	}

	s.definitions["sales-by-customer"] = ReportDefinition{
		Code:        "sales-by-customer",
		Name:        "Laporan Penjualan per Pelanggan",
		Module:      "sales",
		Description: "Analisis kontribusi penjualan, margin, dan profit per customer",
		Parameters: []ReportParameter{
			{Name: "date_from", Label: "Tanggal Mulai", Type: "date", Required: false},
			{Name: "date_to", Label: "Tanggal Selesai", Type: "date", Required: false},
		},
	}

	s.definitions["sales-summary"] = ReportDefinition{
		Code:        "sales-summary",
		Name:        "Ringkasan Laporan Penjualan",
		Module:      "sales",
		Description: "Ringkasan eksekutif penjualan bulanan/tahunan",
		Parameters: []ReportParameter{
			{Name: "year", Label: "Tahun", Type: "integer", Required: false},
			{Name: "month", Label: "Bulan", Type: "integer", Required: false},
		},
	}

	s.definitions["stock-summary"] = ReportDefinition{
		Code:        "stock-summary",
		Name:        "Ringkasan Saldo & Nilai Stok",
		Module:      "inventory",
		Description: "Ringkasan saldo fisik persediaan di gudang beserta HPP dan total nilai aset",
		Parameters: []ReportParameter{
			{Name: "date", Label: "Tanggal Posisi", Type: "date", Required: false},
		},
	}

	s.definitions["receivables-aging"] = ReportDefinition{
		Code:        "receivables-aging",
		Name:        "Umur Piutang Usaha (AR Aging)",
		Module:      "finance",
		Description: "Analisis penuaan piutang penjualan kepada pelanggan",
		Parameters: []ReportParameter{
			{Name: "date", Label: "Tanggal Posisi", Type: "date", Required: false},
		},
	}

	s.definitions["payables-aging"] = ReportDefinition{
		Code:        "payables-aging",
		Name:        "Umur Utang Dagang (AP Aging)",
		Module:      "purchasing",
		Description: "Analisis penuaan saldo utang pembelian kepada supplier",
		Parameters: []ReportParameter{
			{Name: "date", Label: "Tanggal Posisi", Type: "date", Required: false},
		},
	}

	s.definitions["profit-loss"] = ReportDefinition{
		Code:        "profit-loss",
		Name:        "Laporan Laba Rugi Komprehensif",
		Module:      "finance",
		Description: "Laporan kinerja pendapatan, beban, dan laba bersih periode berjalan",
		Parameters: []ReportParameter{
			{Name: "period", Label: "Tahun / Periode", Type: "string", Required: false},
		},
	}

	s.definitions["balance-sheet"] = ReportDefinition{
		Code:        "balance-sheet",
		Name:        "Laporan Neraca (Posisi Keuangan)",
		Module:      "finance",
		Description: "Laporan posisi aset, liabilitas, dan ekuitas perusahaan",
		Parameters: []ReportParameter{
			{Name: "period", Label: "Tahun / Periode", Type: "string", Required: false},
		},
	}

	s.definitions["cashflow-statement"] = ReportDefinition{
		Code:        "cashflow-statement",
		Name:        "Laporan Arus Kas (Cash Flow)",
		Module:      "finance",
		Description: "Laporan arus kas masuk dan keluar aktivitas operasi, investasi, dan pendanaan",
		Parameters: []ReportParameter{
			{Name: "period", Label: "Tahun / Periode", Type: "string", Required: false},
		},
	}

	s.definitions["voucher-print"] = ReportDefinition{
		Code:        "voucher-print",
		Name:        "Bukti Pembayaran / Voucher Kas Bank",
		Module:      "purchasing",
		Description: "Formulir bukti pembayaran dan pengeluaran kas atau bank (Payment Voucher)",
		Parameters: []ReportParameter{
			{Name: "id", Label: "ID Voucher", Type: "integer", Required: false},
		},
	}

	s.definitions["lhpp-print"] = ReportDefinition{
		Code:        "lhpp-print",
		Name:        "Laporan Harian Penagihan Piutang (LHPP)",
		Module:      "distribution",
		Description: "Lembar penagihan piutang harian salesman atau driver ekspedisi",
		Parameters: []ReportParameter{
			{Name: "id", Label: "ID LHPP", Type: "integer", Required: false},
		},
	}

	s.definitions["pos-receipt"] = ReportDefinition{
		Code:        "pos-receipt",
		Name:        "Struk Kasir POS Ritel",
		Module:      "sales",
		Description: "Struk belanja transaksi kasir format kasir ritel",
		Parameters: []ReportParameter{
			{Name: "order_id", Label: "ID Transaksi POS", Type: "integer", Required: false},
		},
	}

	s.definitions["shift-print"] = ReportDefinition{
		Code:        "shift-print",
		Name:        "Rekap Tutup Shift Kasir",
		Module:      "cashier",
		Description: "Rekapitulasi penutupan shift kasir mencakup modal awal, tunai, non-tunai, dan selisih kas",
		Parameters: []ReportParameter{
			{Name: "shift_id", Label: "ID Shift", Type: "integer", Required: false},
		},
	}

	s.definitions["stock-valuation"] = ReportDefinition{
		Code:        "stock-valuation",
		Name:        "Laporan Valuasi Persediaan",
		Module:      "inventory",
		Description: "Laporan valuasi nilai persediaan gudang berdasarkan harga pokok rata-rata dan kuantitas fisik",
		Parameters: []ReportParameter{
			{Name: "date", Label: "Tanggal Posisi", Type: "date", Required: false},
		},
	}

	s.definitions["stock-movements"] = ReportDefinition{
		Code:        "stock-movements",
		Name:        "Laporan Mutasi Persediaan",
		Module:      "inventory",
		Description: "Laporan riwayat arus mutasi stok masuk dan keluar beserta saldo akhir persediaan",
		Parameters: []ReportParameter{
			{Name: "date_from", Label: "Tanggal Dari", Type: "date", Required: false},
			{Name: "date_to", Label: "Tanggal Sampai", Type: "date", Required: false},
		},
	}

	s.definitions["stock-card"] = ReportDefinition{
		Code:        "stock-card",
		Name:        "Kartu Stok Persediaan",
		Module:      "inventory",
		Description: "Laporan pergerakan kartu stok per barang tertentu",
		Parameters: []ReportParameter{
			{Name: "product_id", Label: "ID Produk", Type: "integer", Required: false},
		},
	}

	s.definitions["consolidated-report"] = ReportDefinition{
		Code:        "consolidated-report",
		Name:        "Laporan Konsolidasi Cabang",
		Module:      "finance",
		Description: "Ringkasan kinerja operasional multi-cabang mencakup jumlah order, omset, dan sisa piutang",
		Parameters: []ReportParameter{
			{Name: "year", Label: "Tahun", Type: "integer", Required: false},
		},
	}

	s.definitions["trial-balance"] = ReportDefinition{
		Code:        "trial-balance",
		Name:        "Neraca Saldo (Trial Balance)",
		Module:      "finance",
		Description: "Daftar saldo debit dan kredit seluruh akun buku besar periode berjalan",
		Parameters: []ReportParameter{
			{Name: "date_from", Label: "Tanggal Dari", Type: "date", Required: false},
			{Name: "date_to", Label: "Tanggal Sampai", Type: "date", Required: false},
		},
	}

	s.definitions["purchases-by-supplier"] = ReportDefinition{
		Code:        "purchases-by-supplier",
		Name:        "Laporan Pembelian per Pemasok",
		Module:      "finance",
		Description: "Laporan rekapitulasi pembelian dan saldo hutang per pemasok",
		Parameters: []ReportParameter{
			{Name: "date_from", Label: "Tanggal Mulai", Type: "date", Required: false},
			{Name: "date_to", Label: "Tanggal Selesai", Type: "date", Required: false},
		},
	}

	s.definitions["purchases-by-product"] = ReportDefinition{
		Code:        "purchases-by-product",
		Name:        "Laporan Pembelian per Barang",
		Module:      "finance",
		Description: "Laporan rekapitulasi kuantitas dan nilai pembelian per item barang",
		Parameters: []ReportParameter{
			{Name: "date_from", Label: "Tanggal Mulai", Type: "date", Required: false},
			{Name: "date_to", Label: "Tanggal Selesai", Type: "date", Required: false},
		},
	}
}

// ListDefinitions returns all registered report definitions.
func (s *Service) ListDefinitions() []ReportDefinition {
	defs := make([]ReportDefinition, 0, len(s.definitions))
	for _, d := range s.definitions {
		defs = append(defs, d)
	}
	return defs
}

// GetDefinition returns definition by code.
func (s *Service) GetDefinition(code string) (*ReportDefinition, error) {
	d, ok := s.definitions[code]
	if !ok {
		return nil, ErrInvalidReportCode
	}
	return &d, nil
}

// ListTemplates returns templates filtered by code and status.
func (s *Service) ListTemplates(ctx context.Context, reportCode, status string) ([]ReportTemplate, error) {
	if s.repo == nil {
		return []ReportTemplate{}, nil
	}
	return s.repo.List(ctx, reportCode, status)
}

// GetTemplate returns template by ID.
func (s *Service) GetTemplate(ctx context.Context, id int64) (*ReportTemplate, error) {
	return s.repo.GetByID(ctx, id)
}

// GetActiveTemplate returns active template for code.
func (s *Service) GetActiveTemplate(ctx context.Context, reportCode string) (*ReportTemplate, error) {
	return s.repo.GetActiveByCode(ctx, reportCode)
}

// CreateTemplate creates a new template version.
func (s *Service) CreateTemplate(ctx context.Context, in CreateTemplateInput, userID *int64) (*ReportTemplate, error) {
	if in.ReportCode == "" {
		return nil, ErrInvalidReportCode
	}
	if len(in.TemplateData) == 0 {
		return nil, ErrEmptyTemplateData
	}

	def, exists := s.definitions[in.ReportCode]
	if !exists {
		return nil, fmt.Errorf("%w: %s", ErrInvalidReportCode, in.ReportCode)
	}

	sample := in.SampleData
	if len(sample) == 0 {
		sample = def.SampleData
	}

	tpl := &ReportTemplate{
		ReportCode:   in.ReportCode,
		Name:         in.Name,
		Description:  in.Description,
		TemplateData: in.TemplateData,
		SampleData:   sample,
		Status:       in.Status,
		CreatedBy:    userID,
		UpdatedBy:    userID,
	}
	if tpl.Name == "" {
		tpl.Name = fmt.Sprintf("%s Custom", def.Name)
	}

	err := s.repo.Create(ctx, tpl)
	if err != nil {
		return nil, err
	}
	return tpl, nil
}

// UpdateTemplate updates a draft or active template.
func (s *Service) UpdateTemplate(ctx context.Context, id int64, in UpdateTemplateInput, userID *int64) (*ReportTemplate, error) {
	err := s.repo.Update(ctx, id, &in, userID)
	if err != nil {
		return nil, err
	}
	return s.repo.GetByID(ctx, id)
}

// ActivateTemplate activates a template version.
func (s *Service) ActivateTemplate(ctx context.Context, id int64, userID *int64) error {
	return s.repo.Activate(ctx, id, userID)
}

// DuplicateTemplate creates a new draft version from an existing one.
func (s *Service) DuplicateTemplate(ctx context.Context, id int64, userID *int64) (*ReportTemplate, error) {
	return s.repo.Duplicate(ctx, id, userID)
}
