using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace EF_SQLite
{
    class BazaDanychOsóbDbContext : DbContext
    {
        public DbSet<Osoba> Osoby {  get; set; }
        public DbSet<Adres> Adresy {  get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite("Data Source=osoby.db");            
        }
    }

    public class BazaDanychOsób : IDisposable
    {
        private BazaDanychOsóbDbContext dbc = new BazaDanychOsóbDbContext();

        public BazaDanychOsób()
        {
            dbc.Database.EnsureCreated();
        }

        public void Dispose() 
        {
            dbc.Dispose();
        }

        #region Debugowanie
        public Osoba[] Osoby { get { return dbc.Osoby.ToArray(); } }
        public Adres[] Adresy { get { return dbc.Adresy.ToArray(); } }
        #endregion

        public Osoba PobierzOsoba(int idOsoby)
        {
            return dbc.Osoby.FirstOrDefault(o => o.Id == idOsoby);
        }

        public Osoba this[int idOsoby]
        {
            get => PobierzOsoba(idOsoby);
            set => ZmieńDaneOsoby(idOsoby, value);
        }

        public int[] IdentyfikatoryOsób
        {
            get
            {
                return dbc.Osoby.Select(o => o.Id).ToArray();
            }
        }

        //CRUD = create, read, update, delete
        public int DodajOsobę(Osoba osoba) //nie ma gwarancji zachowania przekazanego Id
        {
            if (osoba == null)
                throw new ArgumentException("Podano pustą referencję jako argument");
            if (osoba.Adres == null) throw new ArgumentException("Brak adresu zamieszkania");

            if (dbc.Osoby.ToArray().Any(o => o.Equals(osoba))) //TODO: Czy można pominąć ToArray
                return osoba.Id;
            else
            {
                //musimy się upewnić, że nie mamy osoby o tym samym Id
                if (dbc.Osoby.Any(o => o.Id == osoba.Id))
                    osoba.Id = dbc.Osoby.Max(o => o.Id) + 1;                
            }

            Adres adres = dbc.Adresy.ToArray().FirstOrDefault(a => a.Equals(osoba.Adres)); //czy można pominąć ToArray
            if (adres != null) osoba.Adres = adres;
            //else dbc.Adresy.Add(osoba.Adres); //TODO: wtf

            //zmiany w bazie danych
            dbc.Osoby.Add(osoba);
            dbc.SaveChanges();

            return osoba.Id;
        }

        private Adres[] pobierzNieużywaneAdresy()
        {
            int[] używaneIdentyfikatoryAdresów = dbc.Osoby.Select(o => o.Adres.Id).Distinct().ToArray();
            List<Adres> nieużywaneAdresy = new List<Adres>();
            foreach(Adres adres in dbc.Adresy)
            {
                if (!używaneIdentyfikatoryAdresów.Contains(adres.Id))
                    nieużywaneAdresy.Add(adres);
            }
            return nieużywaneAdresy.ToArray();
        }

        private void usuńNieużywaneAdresy()
        {
            dbc.Adresy.RemoveRange(pobierzNieużywaneAdresy());
            dbc.SaveChanges();
        }

        public void UsuńOsobę(int idOsoby)
        {
            Osoba osoba = PobierzOsoba(idOsoby);
            if (osoba != null)
            {
                dbc.Osoby.Remove(osoba);
                dbc.SaveChanges();
                usuńNieużywaneAdresy();
            }
        }

        public void ZmieńDaneOsoby(int idOsoby, Osoba noweDane)
        {
            Osoba osoba = PobierzOsoba(idOsoby);
            if (osoba == null) throw new Exception("Nie ma osoby o podanym id");
            osoba.Imię = noweDane.Imię;
            osoba.Nazwisko = noweDane.Nazwisko;
            osoba.NumerTelefonu = noweDane.NumerTelefonu;
            osoba.Adres = noweDane.Adres;
            dbc.SaveChanges();
        }
    }
}
