using DocumentFormat.OpenXml.EMMA;
using DocumentFormat.OpenXml.Office.CustomUI;
using DocumentFormat.OpenXml.Office2010.Excel;
using FamilyTreeAPI.Entities;
using FamilyTreeAPI.Helper;
using FamilyTreeAPI.Interface;
using FamilyTreeAPI.Models;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections;
using System.Collections.Generic;
//using System.Drawing;
using System.Linq;
using System.Threading.Tasks;

namespace FamilyTreeAPI.Repository;

public partial class PersonRepository : IPerson
{
    private readonly FamilyTreeDBContext _context;
    private readonly IRelationShipd _relationship;
    private readonly IHttpContextAccessor _httpContext;
    private readonly IConfiguration _config;
    const string dateFormat = "yyyy-MM-dd";
    public PersonRepository(IConfiguration config, FamilyTreeDBContext context,
        IRelationShipd relationship,
        IHttpContextAccessor httpContext)
    {
        _context = context;
        _relationship = relationship;
        _config = config;
        _httpContext = httpContext;
    }

    private PersonDto FillDto(Person model) 
    {
        PersonDto dto = new();
        dto.Id = model.Id;
        dto.FirstName = model.FirstName;
        dto.LastName = model.LastName;
        dto.Email = model.Email;
        dto.Address = model.Address;
        dto.Phone = model.Phone;
        dto.Alive = model.Alive;
        dto.Image = model.Image;
        dto.FatherId = model.FatherId;
        dto.MotherId = model.MotherId;
        dto.Sex = model.Sex;
        dto.dob = Helpers.DateToStr(model.dob);
        /*
        dto.AddedOn = model.AddedOn;
        
        dto.RoleType = model.RoleType;
                    
        */
      
        return dto;
    }
    private Person FillModel(Person model, PersonDto dto)
    {

        model.FirstName = dto.FirstName;
        model.LastName = dto.LastName;
        model.Email = dto.Email;
        model.Address = dto.Address;
        model.Phone = dto.Phone;
        model.Alive = dto.Alive;
        model.Image = dto.Image;
        model.FatherId = dto.FatherId;
        model.MotherId = dto.MotherId;
        model.Sex = dto.Sex;
        model.dob = Helpers.DateToDateTime(dto.dob);
            
        return model;
    }

    public async Task<ResultModel<Dictionary<int, PersonDto>>> GetDicFamily()
    {

        Dictionary<int, PersonDto> list = new();
        int statuscode = 0;
        string error = "";
        PersonDto item;
        Person jitem;
        try
        {
            //list = await _context.LoadStaffAsync(criteria);           
            var jlist = await _context.Persons.ToListAsync();
            for (int i = 0; i < jlist.Count; i++)
            {
                jitem = jlist[i];
                item = FillDto(jitem);
                list.Add(item.Id,item);
            }
            statuscode = 1;
            //list.Sort((x, y) => x.Code.CompareTo(y.Code));
        }
        catch (Exception ex)
        {
            
            error = ex.ToString();
            statuscode = -1;
          
        }
        return new ResultModel< Dictionary<int, PersonDto> > ()
        {
            Data = list,
            StatusCode = statuscode,
            Message = error
        };
    }

    private async Task<List<PersonDto>> GetChildrens(int FatherId, int MotherId)
    {
        List<PersonDto> list = new();
        PersonDto item;
        Person jitem;
        var jlist = await _context.Persons.Where(x =>
            (x.MotherId == FatherId && x.FatherId == MotherId) || 
            (x.FatherId == FatherId && x.MotherId == MotherId)).ToListAsync();
        for (int i = 0; i < jlist.Count; i++)
        {
            jitem = jlist[i];
            item = FillDto(jitem);
            list.Add(item);
        }
        return list;
    }
    public async Task<ResultModel<List<PersonDto>>> GetChildren(ChildCriteria criteria)
    {
        List<PersonDto> list = new();
        int statuscode = 0;
        string error = "";
        PersonDto item;
        Person jitem;
        try
        {
            //list = await _context.LoadStaffAsync(criteria);           
            list = await GetChildrens(criteria.FatherId, criteria.MotherId);
            statuscode = 1;
            //list.Sort((x, y) => x.Code.CompareTo(y.Code));
        }
        catch (Exception ex)
        {
            
            error = ex.ToString();
            statuscode = -1;
           
        }
        return new ResultModel<List<PersonDto>>()
        {
            Data = list,
            StatusCode = statuscode,
            Message = error
        };
    }

    public async Task<ResultModel<List<PersonDto>>> GetPerson(PersonCriteria criteria)
    {

        List<PersonDto> list = new();
        List<Person>? jlist;
        int statuscode = 0;
        string error = "";
        PersonDto item;
        Person jitem;
        string firstName = criteria.FirstName;
        string lastName = criteria.LastName;
        int Id = criteria.Id;
        try
        {
            //list = await _context.LoadStaffAsync(criteria);           
            if (!string.IsNullOrEmpty(firstName))
            {
                firstName = firstName.ToLower();
                jlist = await _context.Persons.Where(x => 
                    EF.Functions.Like(x.FirstName.ToLower(),firstName + "%")).ToListAsync();    
            }
            else if (!string.IsNullOrEmpty(firstName) && !string.IsNullOrEmpty(lastName))
            {
                firstName = firstName.ToLower();
                lastName = lastName.ToLower();  
                jlist = await _context.Persons.Where(x => EF.Functions.Like(x.FirstName.ToLower(),firstName + "%") 
                                                          && EF.Functions.Like(x.LastName.ToLower(),lastName + "%")).ToListAsync();    
            }
            else
                jlist = await _context.Persons.ToListAsync();
            
            for (int i = 0; i < jlist.Count; i++)
            {
                jitem = jlist[i];
                item = FillDto(jitem);
                list.Add(item);
            }
            statuscode = 1;
            //list.Sort((x, y) => x.Code.CompareTo(y.Code));
        }
        catch (Exception ex)
        {
            
            error = ex.ToString();
            statuscode = -1;
           
        }
        return new ResultModel<List<PersonDto>>()
        {
            Data = list,
            StatusCode = statuscode,
            Message = error
        };
    }

    private async Task<Person> GetPerson(int id)
    {
        Person rval = await _context.Persons.FindAsync(id);
        return rval;    
    }

    private string getStyleClass(string va)
    {
        string result = "";
        //result = "bg-indigo-500 text-black";
        
        if (va == "T")
        {
            result = "bg-indigo-500 text-white";
        }
        else if (va == "P")
        {
            //result = "bg-purple-500 text-black";
            result = "bg-purple-500 text-black";
        }
        else if (va == "C")
        {
            result = "bg-teal-500 text-white";
        } 
        return result;
    }
    public async Task<ResultModel<TreeNode<string>>> GetByFamilyAsync(int id)
    {
        int statuscode = 0;
        string error = "";
        string key = "";
        string data = "";
        string type = "default";
        string pName = "";
        Person person;
        TreeNode<string> citem;
        TreeNode<string> child;
        TreeNode<string> node = new();
        node.Children = new();
        PersonDto dto = new();
        try
        {
            var item = await _context.Persons.FindAsync(id);
            if (item == null)
            {
                statuscode = -1;
            }
            else
            {
               
                node.Label = item.FirstName;
                node.Key = item.Id.ToString();
                node.Type = type;
                node.Icon = "T";
                node.StyleClass = getStyleClass(node.Icon);
                node.Expanded = true;
                node.Data = item.Sex;                
                statuscode = 1;
                dto = FillDto(item);
                await GetPartnerChildrens(dto, node);
            }
        }
        catch (Exception ex)
        {
            error = ex.ToString();
            statuscode = -1;
          
        }
        return new ResultModel<TreeNode<string>> ()
        {
            Data = node,
            StatusCode = statuscode,
            Message = error
        };
    }

    private async Task GetPartnerChildrens(PersonDto person, TreeNode<string> node)
    {
        /*****************************/
        ResultModel<List<RelationShipDto>> rlist = await _relationship.GetByPersonIdAsync(person.Id);
        List<RelationShipDto> relationShips = rlist.Data;
        RelationShipDto relate;
        string type = node.Type;
        TreeNode<string> child;
        PersonDto dto;
        Person pe;
        int fatherId, motherId;
        fatherId = motherId = 0;
        string data = person.Sex;
        TreeNode<string> citem;
        string pName = "";
        string key = "";
        for (int i = 0; i < relationShips.Count; i++)
        {
            relate = relationShips[i];
            if (relate.PersonId == person.Id)
            {
                if (data == "M")
                {
                    fatherId = relate.PersonId;
                    motherId = relate.RelatePersonId;
                   
                }
                else
                {
                    
                    fatherId = relate.RelatePersonId;
                    motherId = relate.PersonId;
                }
            }
            else
            {
                if (data == "M")
                {
                    fatherId = relate.RelatePersonId;
                    motherId = relate.PersonId;
                }
                else
                {
                    fatherId = relate.PersonId;
                    motherId = relate.RelatePersonId;
                }
            }
            //get children
            List<PersonDto> children = await GetChildrens(fatherId, motherId);
            if (children.Count > 0)
            {
                if (person.Id != motherId)
                {
                    //kham
                    if (motherId > 0)
                    {
                        pe = await GetPerson(motherId);
                        pName = pe.FirstName;
                        key = motherId.ToString();
                        data = pe.Sex;
                    }
                }
                else
                {
                    if (fatherId > 0)
                    {
                        pe = await GetPerson(fatherId);
                        pName = pe.FirstName;
                        key = fatherId.ToString();
                        data = pe.Sex;
                    }
                }

                citem = new TreeNode<string>();
                citem.Label = pName;
                citem.Icon = "P";
                citem.StyleClass = getStyleClass(citem.Icon);
                citem.Expanded = true;
                citem.Data = data;
                citem.Type = type;
                citem.Key = key;
                citem.Children = new List<TreeNode<string>>();
                node.Children.Add(citem);
                for (int j = 0; j < children.Count; j++)
                {
                    dto = children[j];
                    child = new TreeNode<string>();
                    child.Expanded = true;
                    child.Type = type;
                    child.Icon = "C";
                    child.StyleClass = getStyleClass(child.Icon);
                    child.Label = dto.FirstName;
                    child.Key = dto.Id.ToString();
                    child.Data = dto.Sex;
                    child.Children = new();
                    citem.Children.Add(child);
                    //get child partner and repeat this.
                    await GetPartnerChildrens(dto, child);
                }
            }

        }

        /*****************************************/
    }

    public async Task<ResultModel<PersonDto>> GetByIdAsync(int id)
    {
        int statuscode = 0;
        string error = "";
        PersonDto dto = new();
        try
        {
            var item = await _context.Persons.FindAsync(id);
            if (item == null)
            {
                statuscode = -1;
            }
            else
            {
                dto = FillDto(item);
                statuscode = 1;
                ResultModel<List<RelationShipDto>> rlist = await _relationship.GetByPersonIdAsync(dto.Id);
                dto.RelationShips = rlist.Data;
                statuscode = rlist.StatusCode;
                error = rlist.Message;
            }
        }
        catch (Exception ex)
        {
            error = ex.ToString();
            statuscode = -1;
          
        }
        return new ResultModel<PersonDto>()
        {
            Data = dto,
            StatusCode = statuscode,
            Message = error
        };
    }
    private string GetDateTimeNow()
    {
        return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
    }

    /// <summary>
    /// need filename to get extension file and family name for id combine to 
    /// name to table.
    /// </summary>
    /// <param name="fileName"></param>
    /// <param name="familyId"></param>
    /// <param name="base64"></param>
    private string SaveFile(string fileName, int familyId, string base64)
    {
        string path = "";
        
        int statusCode = 0;
        string error = "";
        string sdate = GetCurrentDateTime();
        string filename, extention;
        filename = extention = "";
        int first = base64.IndexOf("base64,") +"base64,".Length;
        int last = base64.Length;
        string rbase = base64.Substring(first, last - first);
        
        byte[] newBytes = Convert.FromBase64String(rbase);
        path = _config["AppSettings:ImageFolder"];
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }
        extention = System.IO.Path.GetExtension(fileName);
        filename =  familyId + "_" + sdate + extention;
        string fullpath = System.IO.Path.Combine(path, filename);
        /* 
         using (var fileStream = new FileStream(fullpath, FileMode.Create))
         {
             StreamWriter writer = new StreamWriter(fileStream);
             writer.Write(newBytes);
             //writer.BaseStream.Write(bytes, 0, bytes.Length);

         }
         */
        /*
         using (MemoryStream ms = new MemoryStream(newBytes))
        {
           Image image = Image.FromStream(ms);
           image.Save(fullpath);

        }
        */
        using (FileStream fileStream = new FileStream(fullpath, FileMode.Create, FileAccess.Write, FileShare.None))
        {
            fileStream.Write(newBytes, 0, newBytes.Length);
        }

        return filename;
    }
    public async Task<ResultModel<int>> SaveAsync(PersonForSave container)
    {
        int result = default(int);
        int statuscode = 0;
        string error = "";

        HttpContext? httpContext = _httpContext.HttpContext;
        string loginName = "";
        if (httpContext != null)
        {
            UserDto? user = (UserDto?)httpContext.Items["User"];
            if (user != null)
                loginName = user.FirstName + " " + user.LastName;
        }
        PersonDto item = container.Person;
        try
        {

           
            Person model;
            if (item.Id < 1)
            {
                model = new();
                model = FillModel(model, item);

                // model.Active = true;
                // model.AddedBy = loginName;
                // model.AddedOn = DateTime.Now;
                _context.Persons.Add(model);
                var successid = await _context.SaveChangesAsync();
                result = model.Id;
                if (!string.IsNullOrEmpty(container.FileName))
                {
                    
                    string image = SaveFile(container.FileName, result, container.FormData);
                    // update the image in table to new 
                    model.Image = image;
                    await _context.SaveChangesAsync();
                }
            }
            else
            {
                var model1 = await _context.Persons.FindAsync(item.Id);
                if (model1 != null)
                {
                    model1 = FillModel(model1, item);

                    result = item.Id;
                    if (!string.IsNullOrEmpty(container.FileName))
                    {
                        string image = SaveFile(container.FileName, result, container.FormData);
                        // update the image in table to new 
                        model1.Image = image;
                    }
                    var successid = await _context.SaveChangesAsync();
                }
                //   model.LastModified = DateTime.Now;
                //   model.LastModifiedId = loginName;
            }

            statuscode = 1;
            if (item.RelationShips != null)
            {
                ResultModel<int> rresult = await this._relationship.SaveAsync(item.RelationShips);
                statuscode = rresult.StatusCode;
                error += rresult.Message;
            }
        }
        catch (Exception ex)
        {
            error = ex.ToString();
            statuscode = -1;
        }
        //var dto = await Task.Run(() => result);
        return new ResultModel<int>()
        {
            Data = result,
            StatusCode = statuscode,
            Message = error
        };
    }

    public async Task<ResultModel<int>> DeleteAsync(int id)
    {
        int result = 1;
        int statuscode = 0;
        string error = "";
        try
        {
            var model = await _context.Persons.FindAsync(id);
            if (model != null)
               _context.Persons.Remove(model);

            var successCount = await _context.SaveChangesAsync();
            statuscode = 1;
            result = 1;
        }
        catch (Exception ex)
        {
            error = ex.ToString();
            statuscode = -1;
           
        }
        //var dto = await Task.Run(() => result);
        return new ResultModel<int>()
        {
            Data = result,
            StatusCode = statuscode,
            Message = error
        };
    }

    public Task<ResultModel<string>> UploadImage(UploadCriteria criteria) {
        return UploadImagep(criteria);
    }

    private string GetCurrentDateTime()
    {
        DateTime now = DateTime.Now;
        return now.ToString("yyyyMMdd");
    }

    private async Task<ResultModel<string>> UploadImagep(UploadCriteria criteria)
    {
        string path = "";
        
        int statusCode = 0;
        string error = "";
        string sdate = GetCurrentDateTime();
        string filename, extention;
        filename = extention = "";
        try
        {
            if (criteria.File.Length > 0)
            {
                path = _config["AppSettings:ImageFolder"];
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                extention = System.IO.Path.GetExtension(criteria.FileName);
                filename =  criteria.FamilyId + "_" + sdate + extention;

                using (var fileStream = new FileStream(System.IO.Path.Combine(path, filename), FileMode.Create))
                {
                    await criteria.File.CopyToAsync(fileStream);
                }
               
                statusCode = 1;
            }
            else
            {
               
                statusCode = -1;
                error = "file contain is empty";
            }
        }
        catch (Exception ex)
        {
            statusCode = -1;
            error = ex.Message;

        }

        return new ResultModel<string>()
        {
            Data = filename,
            StatusCode = statusCode,
            Message = error
        };
    }

    public ResultModel<int> DeleteUploadFile(DeleteFileCriteria criteria)
    {
        int result = -1;
        int statusCode = 0;
        string error = "";
        var filePath = _config["AppSettings:ImageFolder"];
        var myfile = filePath + "\\" + criteria.Filename;
        try
        {
            File.Delete(myfile);
            result = 1;
            
            statusCode = 1;
            Person? model = _context.Persons.Find(criteria.FamilyId);
            if (model != null)
            {
                model.Image = null;
                _context.SaveChanges();
            }
        }
        catch (Exception e)
        {
            result = -1;
            statusCode = -1;
            error += e.Message;
        }
        return new ResultModel<int>()
        {
            Data = result,
            StatusCode = statusCode,
            Message = error
        };
    }
}
