// Copyright 2013-2023, AFI.Inc. All rights reserved.

using System;
using System.Collections.Generic;
using BackEnd;
using UnityEngine;

// BackendManager는 뒤끝서버를 관리하는 클래스입니다.
public class BackendManager : MonoBehaviour {
    public List<CharacterProfile> profileList = new List<CharacterProfile>();

    // 캐릭터 프로필 및 기본 스텟 테이블 이름
    private const string _tableName = "USER_PROFILE";

    public static BackendManager Instance { get; private set; }

    // 비동기 함수 호출후에 발생할 이벤트
    public delegate void AfterCallback(bool isSuccess, string message);

    // 현재 내 프로필 데이터
    public UserData currentUserData;
    
    // 내 프로필 데이터의 inDate(고유 ID)
    string _userDataInDate = string.Empty;
    
    void Awake() {
        if (Instance == null) {
            Instance = this;
        }
    }

    // Start is called before the first frame update
    void Start() {
        
        // 뒤끝 초기화 진행
        // useAsyncPoll을 true로 하여 비동기 함수 콜백이 메인쓰레드에서도 작동하게끔 호출
        var bro = Backend.Initialize(true);

        if (bro.IsSuccess()) {
            Debug.Log("뒤끝 초기화 성공");
        } else {
            Debug.LogError("뒤끝 초기화 실패 : " + bro.ToString());
        }
    }

    // Update is called once per frame
    void Update() {
        // 콜백 풀링
        if (Backend.IsInitialized && Backend.UseAsyncQueuePoll) {
            Backend.AsyncPoll();
        }
    }
  
    // 캐릭터 리스트 불러오기
    public void GetCharacterList(AfterCallback afterLoad) {
        
        // 기존에 있던 캐릭터 프로필 데이터 초기화
        profileList.Clear();
        
        // 내가 소유중인 캐릭터 리스트 불러오기
        Backend.MultiCharacter.Character.GetCharacterList(_tableName, callback => {
            try {
                Debug.Log($" Backend.MultiCharacter.Character.GetCharacterList({_tableName}) : {callback}");

                if (callback.IsSuccess() == false) {
                    afterLoad(false, callback.ToString());
                    return;
                }

                foreach (LitJson.JsonData userDataJson in callback.GetReturnValuetoJSON()["characters"]) {
                    CharacterProfile characterProfile = new CharacterProfile(userDataJson, _tableName);
                    profileList.Add(characterProfile);
                }

                afterLoad(true, string.Empty);
            } catch (Exception e) {
                Debug.LogError(e);
                afterLoad(false, e.Message);
            }
        });
    }
    
    // 캐릭터 생성하기
    public void CreateCharacter(string nickname, UserData userData, AfterCallback afterCallback) {
        Backend.MultiCharacter.Character.CreateCharacter(nickname, callback => {
            try {
                Debug.Log($" Backend.MultiCharacter.Character.CreateCharacter({nickname}) : {callback}");

                if (callback.IsSuccess() == false)  {
                    afterCallback(false, callback.ToString());
                    return;
                }
                
                // 캐릭터 생성 시 리턴값으로 로그인에 필요한 uuid와 inDate 제공
                // 캐릭터 생성하자마자 로그인하는 로직 구현 가능
                string inDate = callback.GetReturnValuetoJSON()["gamerInDate"].ToString();
                string uuid = callback.GetReturnValuetoJSON()["uuid"].ToString();

                // 캐릭터 선택하여 로그인
                SelectCharacter(uuid, inDate, ( selectSuccess,  selectMessage) => {

                    if (selectSuccess == false) {
                        afterCallback(false, selectMessage);
                        return;
                    }
                    
                    // 캐릭터 선택하여 로그인 후, 데이터 저장
                    InsertData(userData, ( insertSuccess,  insertMessage) => {
                        profileList.Insert(0,new CharacterProfile(uuid, inDate, nickname, userData));
                        afterCallback(insertSuccess, insertMessage);
                    });
                });
            } catch (Exception e) {
                Debug.LogError(e);
                afterCallback(false, e.Message);
            }
        });
    }

    // 현재 선택된 캐릭터 변경(로컬에 저장된 profileList로)
    public void SelectCharacter(int index, AfterCallback afterCallback) {
        if (index < 0 || index >= profileList.Count) {
            afterCallback(false, "올바른 캐릭터를 선택해주세요");
            return;
        }
        
        SelectCharacter(profileList[index].uuid, profileList[index].inDate, afterCallback);
    }
    
    // 캐릭터 선택(uuid와 inDate를 입력)
    public void SelectCharacter(string uuid, string inDate, AfterCallback afterCallback) {
        Backend.MultiCharacter.Character.SelectCharacter(uuid, inDate, callback2 => {
            Debug.Log($"Backend.MultiCharacter.Character.SelectCharacter({uuid}, {inDate}) : {callback2}");
            if (callback2.IsSuccess() == false) {
                afterCallback(false, callback2.ToString());
                return;
            }

            afterCallback(true, string.Empty);
        });
    }

    // 데이터 생성
    // 캐릭터 생성에는 성공했지만 초기 데이터를 생성하지 못했을 경우 해당 함수로 데이터 서버에 생성
    public void InsertData(UserData userData, AfterCallback afterCallback) {
        Backend.GameData.Insert(_tableName, userData.GetParam(), callback => {
            try {
                Debug.Log($"Backend.GameData.Insert({_tableName}) : {callback}");

                if (callback.IsSuccess() == false) {
                    if(callback.GetMessage().StartsWith("table not found")) {
                        afterCallback(false, $"테이블이 존재하지 않습니다.\n뒤끝 콘솔에서 {_tableName}을 생성해 주세요.");
                        return;
                    }
                    afterCallback(false, callback.ToString());
                    return;
                }
                
                _userDataInDate = callback.GetInDate();
                afterCallback(true, string.Empty);
            } catch (Exception e) {
                Debug.LogError(e);
                afterCallback(false, e.Message);
            }
        });
    }

    // 데이터 불러오기
    public void GetUserData(AfterCallback afterCallback) {
        Backend.GameData.Get(_tableName, new Where(), callback => {
            try {
                Debug.Log($"Backend.GameData.Get({_tableName}) : {callback}");

                if (callback.IsSuccess() == false) {
                    afterCallback(false, callback.ToString());
                    return;
                }

                LitJson.JsonData jsonData = callback.FlattenRows();
                if (jsonData.Count <= 0) {
                    afterCallback(true, "데이터가 존재하지 않습니다.");
                } else {
                    _userDataInDate = jsonData[0]["inDate"].ToString();
                    currentUserData = new UserData(jsonData[0]);
                    afterCallback(true, string.Empty);
                }


            } catch (Exception e) {
                afterCallback(false, e.Message);
            }
        });
    }

    // 데이터 저장하기
    public void SaveUserData(AfterCallback afterCallback) {
        Backend.GameData.UpdateV2(_tableName, _userDataInDate, Backend.UserInDate, currentUserData.GetParam(), callback => {
            Debug.Log($"Backend.GameData.UpdateV2({_tableName}, {_userDataInDate}, {Backend.UserInDate}) : {callback}");

            if (callback.IsSuccess()) {
                afterCallback(true, string.Empty);
            } else {
                afterCallback(false, callback.ToString());
            }
        });
    }
    
    
    // 데이터 삭제하기
    public void DeleteCharacter(int index, AfterCallback afterCallback) {
        
        if (index < 0 || index >= profileList.Count) {
            afterCallback(false, "올바른 캐릭터를 선택해주세요");
            return;
        }

        string uuid = profileList[index].uuid;
        string inDate = profileList[index].inDate;

        Backend.MultiCharacter.Character.DeleteCharacter(uuid, inDate, callback => {
            Debug.Log($"Backend.MultiCharacter.Character.DeleteCharacter({uuid}, {inDate}): {callback}");

            if (callback.IsSuccess() == false) {
                afterCallback(false, callback.ToString());
                return;
            }

            // 삭제된 데이터는 list에서 제거
            profileList[index] = null;
            profileList.RemoveAt(index);
            afterCallback(true, string.Empty);
        });
    }

    // 계정 로그아웃
    public void LogoutAccount(AfterCallback afterCallback) {
        Backend.MultiCharacter.Account.LogoutAccount(callback => {
            Debug.Log($"Backend.MultiCharacter.Account.LogoutAccount() : {callback}");

            if (callback.IsSuccess() == false) {
                afterCallback(false, callback.ToString());
                return;
            }
            
            afterCallback(true, string.Empty);
        });
    }
    
    // 캐릭터 로그아웃
    public void Logout(AfterCallback afterCallback) {
        Backend.BMember.Logout(callback => {
            Debug.Log($"Backend.BMember.Logout() : {callback}");

            if (callback.IsSuccess() == false) {
                afterCallback(false, callback.ToString());
                return;
            }
            
            afterCallback(true, string.Empty);
        });
    }

    // 계정 탈퇴
    public void WithdrawAccount(AfterCallback afterCallback) {
        Backend.MultiCharacter.Account.WithdrawAccount(callback => {
            Debug.Log($" Backend.MultiCharacter.Account.WithdrawAccount() : {callback}");

            if (callback.IsSuccess() == false) {
                afterCallback(false, callback.ToString());
                return;
            }
            
            afterCallback(true, string.Empty);
        });
    }
}