using Microsoft.AspNetCore.WebUtilities;
using System.Reflection;

namespace SharedKernel.Helpers
{
    public class Pagination
    {
        public int Page { get; set; } = 1; // 현재 페이지
        public int TotalRows { get; set; } = 0; // 전체 항목 수
        public int TotalPage { get; set; } = 0; // 전체 페이지 수
        public int PerPage = 10;

        public int StartPage = 0; // 현재 페이지의 시작 번호
        public int EndPage = 0; // 현재 페이지의 마지막 번호
        public int PageGroupSize = 10; // 한번에 보여줄 페이지 번호 개수
        public int CurrentPageGroup = 0; // 현재 페이지가 속한 페이지 그룹의 시작번호

        public int GroupStartPage = 0; // 페이지 그룹의 시작 페이지 번호
        public int GroupEndPage = 0; // 페이지 그룹의 마지막 페이지 번호

        public int PrevGroupPage = 0; // 이전 페이지 그룹의 페이지 번호
        public int NextGroupPage = 0; // 다음 페이지 그룹 페이지 번호

        private Dictionary<string, string?> QueryStringParams;

        public Pagination(long totalRows, int page, int perPage, object? queryParams) : this((int)totalRows, page, perPage, queryParams) { }
        public Pagination(int totalRows, int page, int perPage, object? queryParams = null)
        {

            Page = page;
            PerPage = perPage;
            TotalRows = Math.Max(totalRows, 0);
            TotalPage = (int)Math.Ceiling((double)TotalRows / perPage);

            CurrentPageGroup = (Page - 1) / PageGroupSize + 1;
            StartPage = (CurrentPageGroup - 1) * PageGroupSize + 1;
            EndPage = Math.Min(StartPage + PageGroupSize - 1, TotalPage);

            GroupStartPage = StartPage;
            GroupEndPage = EndPage;

            PrevGroupPage = Math.Max(StartPage - PageGroupSize, 1); // 이전 페이지 그룹의 첫 페이지 계산
            NextGroupPage = Math.Min(EndPage + 1, TotalPage); // 다음 페이지 그룹의 첫 페이지 계산

            // 객체를 쿼리스트링으로 변환
            QueryStringParams = ToDictionary(queryParams);
        }

        // 이전 페이지 여부
        public bool HasPreviousPage => Page > 1;

        // 다음 페이지 여부
        public bool HasNextPage => Page < TotalPage;

        // 객체를 Dictionary로 변환하는 메서드
        private static Dictionary<string, string?> ToDictionary(object? obj)
        {
            var dictionary = new Dictionary<string, string?>();

            if (obj is null)
            {
                return dictionary;
            }

            // 문자열이면 직접 분리
            if (obj is string str)
            {
                var parsed = QueryHelpers.ParseQuery(str);
                foreach (var row in parsed)
                {
                    dictionary[row.Key] = row.Value.ToString();
                }

                return dictionary;
            }

            // 일반 객체는 reflection을 사용하여 속성들을 가져옴
            var properties = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (var prop in properties)
            {
                var value = prop.GetValue(obj)?.ToString();
                if (!string.IsNullOrEmpty(value))
                {
                    dictionary[prop.Name] = value;
                }
            }

            return dictionary;
        }

        // 최종 쿼리스트링을 반환하는 메서드
        public string BuildQueryString()
        {
            if (QueryStringParams == null || QueryStringParams.Count == 0)
            {
                return "";
            }

            var queryString = QueryHelpers.AddQueryString("", QueryStringParams); // ?key=value&key2=value2
            if (string.IsNullOrEmpty(queryString))
            {
                return "";
            }

            return "&" + queryString.TrimStart('?');
        }
    }
}
