package socket

import (
	"fmt"

	altv1 "git.toki-labs.com/toki/alt/packages/contracts/gen/go/alt/v1"
	"git.toki-labs.com/toki/alt/packages/domain/market"
)

func instrumentToProto(inst market.Instrument) *altv1.Instrument {
	return &altv1.Instrument{
		Id:              string(inst.ID),
		Market:          marketToProto(inst.Market),
		Venue:           venueToProto(inst.Venue),
		Symbol:          inst.Symbol,
		Name:            inst.Name,
		Currency:        currencyToProto(inst.Currency),
		ProviderSymbols: cloneProviderSymbols(inst.ProviderSymbols),
		AssetType:       assetTypeToProto(inst.AssetType),
	}
}

func assetTypeToProto(at market.AssetType) altv1.AssetType {
	switch at {
	case market.AssetTypeEquity:
		return altv1.AssetType_ASSET_TYPE_EQUITY
	case market.AssetTypeETF:
		return altv1.AssetType_ASSET_TYPE_ETF
	default:
		return altv1.AssetType_ASSET_TYPE_UNSPECIFIED
	}
}

func assetTypeFromProto(at altv1.AssetType) (market.AssetType, error) {
	switch at {
	case altv1.AssetType_ASSET_TYPE_UNSPECIFIED:
		return "", nil
	case altv1.AssetType_ASSET_TYPE_EQUITY:
		return market.AssetTypeEquity, nil
	case altv1.AssetType_ASSET_TYPE_ETF:
		return market.AssetTypeETF, nil
	default:
		return "", fmt.Errorf("unsupported asset type %q", at.String())
	}
}

func instrumentsToProto(insts []market.Instrument) []*altv1.Instrument {
	out := make([]*altv1.Instrument, len(insts))
	for i, inst := range insts {
		out[i] = instrumentToProto(inst)
	}
	return out
}

func barToProto(bar market.Bar) *altv1.Bar {
	return &altv1.Bar{
		InstrumentId:    string(bar.InstrumentID),
		Timeframe:       timeframeToProto(bar.Timeframe),
		TimestampUnixMs: timeToUnixMs(bar.Timestamp),
		Open:            priceToProto(bar.Open),
		High:            priceToProto(bar.High),
		Low:             priceToProto(bar.Low),
		Close:           priceToProto(bar.Close),
		Volume:          quantityToProto(bar.Volume),
	}
}

func barsToProto(bars []market.Bar) []*altv1.Bar {
	out := make([]*altv1.Bar, len(bars))
	for i, bar := range bars {
		out[i] = barToProto(bar)
	}
	return out
}

// venueFromProto converts a proto venue to the domain venue. The unspecified
// venue maps to an empty domain venue because an import selector may not pin a
// venue (e.g. a watchlist selector); only out-of-range enum values are an error.
func venueFromProto(v altv1.Venue) (market.Venue, error) {
	switch v {
	case altv1.Venue_VENUE_UNSPECIFIED:
		return "", nil
	case altv1.Venue_VENUE_KRX:
		return market.VenueKRX, nil
	case altv1.Venue_VENUE_NASDAQ:
		return market.VenueNASDAQ, nil
	case altv1.Venue_VENUE_NYSE:
		return market.VenueNYSE, nil
	default:
		return "", fmt.Errorf("unsupported venue %q", v.String())
	}
}

func venueToProto(v market.Venue) altv1.Venue {
	switch v {
	case market.VenueKRX:
		return altv1.Venue_VENUE_KRX
	case market.VenueNASDAQ:
		return altv1.Venue_VENUE_NASDAQ
	case market.VenueNYSE:
		return altv1.Venue_VENUE_NYSE
	default:
		return altv1.Venue_VENUE_UNSPECIFIED
	}
}

func cloneProviderSymbols(symbols map[string]string) map[string]string {
	if len(symbols) == 0 {
		return nil
	}
	out := make(map[string]string, len(symbols))
	for k, v := range symbols {
		out[k] = v
	}
	return out
}
