package services

import (
	"encoding/json"
	"fmt"
)

// AccountService 계좌 조회 서비스 (잔고/미체결/체결내역/주문가능)
type AccountService struct {
	client *KiwoomClient
}

var accountService *AccountService

// GetAccountService 계좌 서비스 싱글턴 반환
func GetAccountService() *AccountService {
	if accountService == nil {
		accountService = &AccountService{client: GetKiwoomClient()}
	}
	return accountService
}

// --- 미체결 ---

// PendingOrder 미체결 주문 항목
type PendingOrder struct {
	OrdNo    string `json:"ordNo"`    // 주문번호
	StkCd    string `json:"stkCd"`    // 종목코드
	StkNm    string `json:"stkNm"`    // 종목명
	OrdQty   string `json:"ordQty"`   // 주문수량
	OrdPric  string `json:"ordPric"`  // 주문가격
	OsoQty   string `json:"osoQty"`   // 미체결수량
	IoTpNm   string `json:"ioTpNm"`   // 주문구분
	TrdeTp   string `json:"trdeTp"`   // 매매구분 (1:매도, 2:매수)
	Tm       string `json:"tm"`       // 시간
	CntrPric string `json:"cntrPric"` // 체결가
	CntrQty  string `json:"cntrQty"`  // 체결량
}

// GetPendingOrders 미체결 주문 조회 (ka10075)
func (s *AccountService) GetPendingOrders() ([]PendingOrder, error) {
	body := map[string]string{
		"all_stk_tp": "0", // 전체종목
		"trde_tp":    "0", // 전체
		"stk_cd":     "",
		"stex_tp":    "0", // 통합
	}

	respBody, err := s.client.post("ka10075", "/api/dostk/acnt", body)
	if err != nil {
		return nil, fmt.Errorf("미체결 조회 실패: %w", err)
	}

	var result struct {
		Oso []struct {
			OrdNo    string `json:"ord_no"`
			StkCd    string `json:"stk_cd"`
			StkNm    string `json:"stk_nm"`
			OrdQty   string `json:"ord_qty"`
			OrdPric  string `json:"ord_pric"`
			OsoQty   string `json:"oso_qty"`
			IoTpNm   string `json:"io_tp_nm"`
			TrdeTp   string `json:"trde_tp"`
			Tm       string `json:"tm"`
			CntrPric string `json:"cntr_pric"`
			CntrQty  string `json:"cntr_qty"`
		} `json:"oso"`
		ReturnCode int    `json:"return_code"`
		ReturnMsg  string `json:"return_msg"`
	}

	if err := json.Unmarshal(respBody, &result); err != nil {
		return nil, fmt.Errorf("미체결 응답 파싱 실패: %w", err)
	}
	if result.ReturnCode != 0 {
		return nil, fmt.Errorf("미체결 조회 오류: %s", result.ReturnMsg)
	}

	orders := make([]PendingOrder, 0, len(result.Oso))
	for _, o := range result.Oso {
		orders = append(orders, PendingOrder{
			OrdNo:    o.OrdNo,
			StkCd:    o.StkCd,
			StkNm:    o.StkNm,
			OrdQty:   o.OrdQty,
			OrdPric:  o.OrdPric,
			OsoQty:   o.OsoQty,
			IoTpNm:   o.IoTpNm,
			TrdeTp:   o.TrdeTp,
			Tm:       o.Tm,
			CntrPric: o.CntrPric,
			CntrQty:  o.CntrQty,
		})
	}
	return orders, nil
}

// --- 체결내역 ---

// OrderHistory 체결내역 항목
type OrderHistory struct {
	OrdNo       string `json:"ordNo"`
	StkNm       string `json:"stkNm"`
	IoTpNm      string `json:"ioTpNm"`
	OrdPric     string `json:"ordPric"`
	OrdQty      string `json:"ordQty"`
	CntrPric    string `json:"cntrPric"`
	CntrQty     string `json:"cntrQty"`
	OsoQty      string `json:"osoQty"`
	TrdeCmsn    string `json:"trdeCmsn"`
	TrdeTax     string `json:"trdeTax"`
	OrdStt      string `json:"ordStt"`
	TrdeTp      string `json:"trdeTp"`
	OrdTm       string `json:"ordTm"`
	StkCd       string `json:"stkCd"`
}

// GetOrderHistory 체결내역 조회 (ka10076)
func (s *AccountService) GetOrderHistory() ([]OrderHistory, error) {
	body := map[string]string{
		"stk_cd":  "",
		"qry_tp":  "0", // 전체
		"sell_tp": "0", // 전체
		"ord_no":  "",
		"stex_tp": "0", // 통합
	}

	respBody, err := s.client.post("ka10076", "/api/dostk/acnt", body)
	if err != nil {
		return nil, fmt.Errorf("체결내역 조회 실패: %w", err)
	}

	var result struct {
		Cntr []struct {
			OrdNo      string `json:"ord_no"`
			StkNm      string `json:"stk_nm"`
			IoTpNm     string `json:"io_tp_nm"`
			OrdPric    string `json:"ord_pric"`
			OrdQty     string `json:"ord_qty"`
			CntrPric   string `json:"cntr_pric"`
			CntrQty    string `json:"cntr_qty"`
			OsoQty     string `json:"oso_qty"`
			TdyTrdeCmsn string `json:"tdy_trde_cmsn"`
			TdyTrdeTax  string `json:"tdy_trde_tax"`
			OrdStt     string `json:"ord_stt"`
			TrdeTp     string `json:"trde_tp"`
			OrdTm      string `json:"ord_tm"`
			StkCd      string `json:"stk_cd"`
		} `json:"cntr"`
		ReturnCode int    `json:"return_code"`
		ReturnMsg  string `json:"return_msg"`
	}

	if err := json.Unmarshal(respBody, &result); err != nil {
		return nil, fmt.Errorf("체결내역 응답 파싱 실패: %w", err)
	}
	if result.ReturnCode != 0 {
		return nil, fmt.Errorf("체결내역 조회 오류: %s", result.ReturnMsg)
	}

	history := make([]OrderHistory, 0, len(result.Cntr))
	for _, c := range result.Cntr {
		history = append(history, OrderHistory{
			OrdNo:    c.OrdNo,
			StkNm:    c.StkNm,
			IoTpNm:   c.IoTpNm,
			OrdPric:  c.OrdPric,
			OrdQty:   c.OrdQty,
			CntrPric: c.CntrPric,
			CntrQty:  c.CntrQty,
			OsoQty:   c.OsoQty,
			TrdeCmsn: c.TdyTrdeCmsn,
			TrdeTax:  c.TdyTrdeTax,
			OrdStt:   c.OrdStt,
			TrdeTp:   c.TrdeTp,
			OrdTm:    c.OrdTm,
			StkCd:    c.StkCd,
		})
	}
	return history, nil
}

// --- 잔고 ---

// BalanceStock 잔고 개별 종목
type BalanceStock struct {
	StkCd       string `json:"stkCd"`
	StkNm       string `json:"stkNm"`
	EvltvPrft   string `json:"evltvPrft"`   // 평가손익
	PrftRt      string `json:"prftRt"`      // 수익률
	PurPric     string `json:"purPric"`     // 매입가
	RmndQty     string `json:"rmndQty"`     // 보유수량
	TrdeAbleQty string `json:"trdeAbleQty"` // 매매가능수량
	CurPrc      string `json:"curPrc"`      // 현재가
	PurAmt      string `json:"purAmt"`      // 매입금액
	EvltAmt     string `json:"evltAmt"`     // 평가금액
}

// BalanceResult 잔고 조회 결과
type BalanceResult struct {
	TotPurAmt      string         `json:"totPurAmt"`      // 총매입금액
	TotEvltAmt     string         `json:"totEvltAmt"`     // 총평가금액
	TotEvltPl      string         `json:"totEvltPl"`      // 총평가손익
	TotPrftRt      string         `json:"totPrftRt"`      // 총수익률
	PrsmDpstAsetAmt string        `json:"prsmDpstAsetAmt"` // 추정예탁자산
	Stocks         []BalanceStock `json:"stocks"`
}

// GetBalance 계좌 잔고 조회 (kt00018)
func (s *AccountService) GetBalance() (*BalanceResult, error) {
	body := map[string]string{
		"qry_tp":       "2", // 개별
		"dmst_stex_tp": "KRX",
	}

	respBody, err := s.client.post("kt00018", "/api/dostk/acnt", body)
	if err != nil {
		return nil, fmt.Errorf("잔고 조회 실패: %w", err)
	}

	var result struct {
		TotPurAmt       string `json:"tot_pur_amt"`
		TotEvltAmt      string `json:"tot_evlt_amt"`
		TotEvltPl       string `json:"tot_evlt_pl"`
		TotPrftRt       string `json:"tot_prft_rt"`
		PrsmDpstAsetAmt string `json:"prsm_dpst_aset_amt"`
		AcntEvltRemnIndvTot []struct {
			StkCd       string `json:"stk_cd"`
			StkNm       string `json:"stk_nm"`
			EvltvPrft   string `json:"evltv_prft"`
			PrftRt      string `json:"prft_rt"`
			PurPric     string `json:"pur_pric"`
			RmndQty     string `json:"rmnd_qty"`
			TrdeAbleQty string `json:"trde_able_qty"`
			CurPrc      string `json:"cur_prc"`
			PurAmt      string `json:"pur_amt"`
			EvltAmt     string `json:"evlt_amt"`
		} `json:"acnt_evlt_remn_indv_tot"`
		ReturnCode int    `json:"return_code"`
		ReturnMsg  string `json:"return_msg"`
	}

	if err := json.Unmarshal(respBody, &result); err != nil {
		return nil, fmt.Errorf("잔고 응답 파싱 실패: %w", err)
	}
	if result.ReturnCode != 0 {
		return nil, fmt.Errorf("잔고 조회 오류: %s", result.ReturnMsg)
	}

	stocks := make([]BalanceStock, 0, len(result.AcntEvltRemnIndvTot))
	for _, s := range result.AcntEvltRemnIndvTot {
		stocks = append(stocks, BalanceStock{
			StkCd:       s.StkCd,
			StkNm:       s.StkNm,
			EvltvPrft:   s.EvltvPrft,
			PrftRt:      s.PrftRt,
			PurPric:     s.PurPric,
			RmndQty:     s.RmndQty,
			TrdeAbleQty: s.TrdeAbleQty,
			CurPrc:      s.CurPrc,
			PurAmt:      s.PurAmt,
			EvltAmt:     s.EvltAmt,
		})
	}

	return &BalanceResult{
		TotPurAmt:       result.TotPurAmt,
		TotEvltAmt:      result.TotEvltAmt,
		TotEvltPl:       result.TotEvltPl,
		TotPrftRt:       result.TotPrftRt,
		PrsmDpstAsetAmt: result.PrsmDpstAsetAmt,
		Stocks:          stocks,
	}, nil
}

// --- 예수금상세 ---

// DepositResult 예수금 상세 결과
type DepositResult struct {
	Entr       string `json:"entr"`       // 예수금
	D2Entra    string `json:"d2Entra"`    // D+2 추정예수금
	OrdAlowAmt string `json:"ordAlowAmt"` // 주문가능금액
}

// GetDepositDetail 예수금 상세 조회 (kt00001)
func (s *AccountService) GetDepositDetail() (*DepositResult, error) {
	body := map[string]string{
		"qry_tp": "3", // 추정조회
	}

	respBody, err := s.client.post("kt00001", "/api/dostk/acnt", body)
	if err != nil {
		return nil, fmt.Errorf("예수금 조회 실패: %w", err)
	}

	var result struct {
		Entr       string `json:"entr"`
		D2Entra    string `json:"d2_entra"`
		OrdAlowAmt string `json:"ord_alow_amt"`
		ReturnCode int    `json:"return_code"`
		ReturnMsg  string `json:"return_msg"`
	}

	if err := json.Unmarshal(respBody, &result); err != nil {
		return nil, fmt.Errorf("예수금 응답 파싱 실패: %w", err)
	}
	if result.ReturnCode != 0 {
		return nil, fmt.Errorf("예수금 조회 오류: %s", result.ReturnMsg)
	}

	return &DepositResult{
		Entr:       result.Entr,
		D2Entra:    result.D2Entra,
		OrdAlowAmt: result.OrdAlowAmt,
	}, nil
}

// --- 주문가능금액 ---

// OrderableResult 주문가능금액/수량
type OrderableResult struct {
	OrdAlowa string `json:"ordAlowa"` // 주문가능현금
	Entr     string `json:"entr"`     // 예수금
	OrdAlowq string `json:"ordAlowq"` // 주문가능수량 (증거금100%)
}

// GetOrderable 주문가능금액/수량 조회 (kt00010)
// 모의투자 환경에서 RC7006 오류 시 kt00001(예수금상세) 폴백
// side: "1"=매도, "2"=매수
func (s *AccountService) GetOrderable(code, price, side string) (*OrderableResult, error) {
	body := map[string]string{
		"stk_cd":  code,
		"trde_tp": side,
		"uv":      price,
	}

	respBody, err := s.client.post("kt00010", "/api/dostk/acnt", body)
	if err != nil {
		// 네트워크 오류 시 예수금상세(kt00001)로 폴백
		return s.orderableFromDeposit()
	}

	var result struct {
		OrdAlowa         string `json:"ord_alowa"`
		Entr             string `json:"entr"`
		Profa100OrdAlowq string `json:"profa_100ord_alowq"`
		ReturnCode       int    `json:"return_code"`
		ReturnMsg        string `json:"return_msg"`
	}

	if err := json.Unmarshal(respBody, &result); err != nil {
		return nil, fmt.Errorf("주문가능금액 응답 파싱 실패: %w", err)
	}
	if result.ReturnCode != 0 {
		// 모의투자 환경(RC7006) 등 kt00010 미지원 시 kt00001 폴백
		return s.orderableFromDeposit()
	}

	return &OrderableResult{
		OrdAlowa: result.OrdAlowa,
		Entr:     result.Entr,
		OrdAlowq: result.Profa100OrdAlowq,
	}, nil
}

// orderableFromDeposit kt00001(예수금상세)로 주문가능금액 조회 (kt00010 폴백)
func (s *AccountService) orderableFromDeposit() (*OrderableResult, error) {
	dep, err := s.GetDepositDetail()
	if err != nil {
		return nil, fmt.Errorf("주문가능금액 조회 실패(폴백): %w", err)
	}
	return &OrderableResult{
		OrdAlowa: dep.OrdAlowAmt,
		Entr:     dep.Entr,
		OrdAlowq: "0", // kt00001은 수량 미제공
	}, nil
}
