// Package lkemulator — имитация ЛК клиента (ESIA Finance). Предоставляет
// HTML-формы для подачи заявок M2M и приёмник callback'ов от lk-gateway.
// Используется для сквозного дев-теста системы Bridge-and-Join-s без
// реальной интеграции с ЛК.
package lkemulator

import (
	"sync"
	"time"
)

// Claim — локальная сохранённая копия заявки + последний callback.
type Claim struct {
	ID                        string
	GatewayID                 string // ID присвоенный lk-gateway (совпадает с m2mcore.Deal.ID)
	Status                    string
	LocalStatus               string // "submitted" | "callback_received" — внутренняя метка эмулятора
	CreatedAt                 time.Time
	UpdatedAt                 time.Time
	InvestorName              string
	TransferringDepositoryINN string
	ReceivingDepositoryINN    string
	SecuritiesCount           int
	LastCallback              *CallbackRecord
	RawRequest                map[string]any
	RawResponse               map[string]any
}

// CallbackRecord — сохранённый callback от lk-gateway.
type CallbackRecord struct {
	NewStatus  string
	ReasonCode string
	ReasonText string
	UpdatedAt  time.Time
	GUID       string
	StatusCode string
	Responses  []CallbackResponseEntry
}

// CallbackResponseEntry — одна строка из NSDResponse в callback'е.
type CallbackResponseEntry struct {
	ReferenceID string
	Code        string
	Text        string
}

// Store — in-memory хранилище заявок эмулятора.
type Store struct {
	mu    sync.RWMutex
	byID  map[string]*Claim
	byGw  map[string]*Claim
	order []string
}

// NewStore — пустое хранилище.
func NewStore() *Store {
	return &Store{byID: make(map[string]*Claim), byGw: make(map[string]*Claim)}
}

// Add сохраняет новую заявку.
func (s *Store) Add(c *Claim) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.byID[c.ID] = c
	if c.GatewayID != "" {
		s.byGw[c.GatewayID] = c
	}
	s.order = append(s.order, c.ID)
}

// Get — по локальному ID.
func (s *Store) Get(id string) *Claim {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.byID[id]
}

// GetByGatewayID — по ID от lk-gateway (используется при приёме callback).
func (s *Store) GetByGatewayID(id string) *Claim {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.byGw[id]
}

// All возвращает все заявки в обратном порядке создания.
func (s *Store) All() []*Claim {
	s.mu.RLock()
	defer s.mu.RUnlock()
	out := make([]*Claim, 0, len(s.order))
	for i := len(s.order) - 1; i >= 0; i-- {
		out = append(out, s.byID[s.order[i]])
	}
	return out
}

// ApplyCallback обновляет заявку при получении callback'а от lk-gateway.
func (s *Store) ApplyCallback(gatewayID string, cb *CallbackRecord) bool {
	s.mu.Lock()
	defer s.mu.Unlock()
	c, ok := s.byGw[gatewayID]
	if !ok {
		return false
	}
	c.Status = cb.NewStatus
	c.LocalStatus = "callback_received"
	c.UpdatedAt = cb.UpdatedAt
	c.LastCallback = cb
	return true
}
