import 'dart:convert';
import 'dart:io';

import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';

import '../../quiosco/models/db_archivos_model.dart';
import '../../quiosco/models/db_categoria_model.dart';
import '../../quiosco/models/db_estante_model.dart';
import '../../quiosco/models/db_secciones_model.dart';
import '../../quiosco/models/response_model.dart';

class DBProvider{
  static Database? _database;
  static final DBProvider db = DBProvider._();

  DBProvider._();

  Future<Database> get database async{
    if(_database != null) return _database!;
    _database = await initDB();

    return _database!;
  }

  initDB() async{
    Directory documentsDirectory = await getApplicationDocumentsDirectory();
    final path = "${documentsDirectory.path}/Quiosco.db";

    return await openDatabase(
      path,
      version: 1,
      onOpen: (db){},
      onCreate: (db, version) async{
        await db.execute('CREATE TABLE login(dinum INTEGER PRIMARY KEY, user TEXT, accessToken TEXT, role TEXT, lsec TEXT, fecha DATE, pass TEXT)');
        await db.execute('CREATE TABLE estantes(ides INTEGER PRIMARY KEY,esta TEXT,acti TEXT,idim TEXT,uscr INTEGER,fecr DATE,usmo INTEGER,femo DATE,'
          'orde INTEGER)');
        await db.execute('CREATE TABLE secciones(idse INTEGER PRIMARY KEY,esta TEXT,ides INTEGER,secc TEXT,acti TEXT,idim TEXT,uscr INTEGER,fecr DATE,'
          'usmo INTEGER,femo DATE,orde INTEGER)');
        await db.execute('CREATE TABLE categorias(idca INTEGER PRIMARY KEY,secc TEXT,esta TEXT,idse INTEGER,cate TEXT,acti TEXT,idim TEXT,uscr INTEGER,'
          'fecr DATE,usmo INTEGER,femo DATE,orde INTEGER)');
        await db.execute('CREATE TABLE archivos(idar INTEGER PRIMARY KEY,esta TEXT,secc TEXT,cate TEXT,ides INTEGER,idse INTEGER,idca INTEGER,arch TEXT,'
          'acti TEXT,arfi TEXT,idim TEXT,uscr INTEGER,fecr DATE,usel TEXT,feel TEXT,orde INTEGER,femo DATE,usmo INTEGER,dinombre TEXT)');

      }
    );
  }

  Future<int> insertLoginInfo(Response response, DateTime fecha, String pass) async{
    final db = await database;
    final res = await db.rawInsert(
        "INSERT OR REPLACE INTO login (dinum, user, accessToken, role, lsec, fecha, pass)"
            "VALUES (${response.result!.dinum}, '${response.result!.user}', '${response.result!.accessToken}', "
            "'${response.result!.role}', '${response.result!.lsec}', '$fecha', '${base64Encode(utf8.encode(pass))}')"
    );
    return res;
  }

  Future<int> newEstanteRaw(DbEstantes dbEstantes) async{
    final db = await database;
    final res = await db.rawInsert(
        "INSERT OR REPLACE INTO estantes (ides, esta, acti, idim, uscr, fecr, usmo, femo, orde)"
            "VALUES (${dbEstantes.ides}, '${dbEstantes.esta}', '${dbEstantes.acti}', '${dbEstantes.idim}', ${dbEstantes.uscr}, '${orderDate(dbEstantes.fecr!)}', "
            "${dbEstantes.usmo}, '${orderDate(dbEstantes.femo!)}', ${dbEstantes.orde})"
    );
    return res;
  }

  Future<int> newSeccionRaw(DbSecciones dbSecciones) async{
    final db = await database;
    final res = await db.rawInsert(
        "INSERT OR REPLACE INTO secciones (idse, esta, ides, secc, acti, idim, uscr, fecr, usmo, femo, orde)"
            "VALUES (${dbSecciones.idse}, '${dbSecciones.esta}', ${dbSecciones.ides},'${dbSecciones.secc}', '${dbSecciones.acti}', '${dbSecciones.idim}', "
            "${dbSecciones.uscr}, '${dbSecciones.fecr}', ${dbSecciones.usmo}, '${dbSecciones.femo}', ${dbSecciones.orde})"
    );
    return res;
  }

  Future<int?> newCategoriaRaw(DbCategorias dbCategorias) async{
    final db = await database;
    try {
      final res = await db.rawInsert(
          "INSERT OR REPLACE INTO categorias (idca, secc, esta, idse, cate, acti, idim, uscr, fecr, usmo, femo, orde)"
              "VALUES (${dbCategorias.idca}, '${dbCategorias
              .secc}', '${dbCategorias.esta}',${dbCategorias
              .idse}, '${dbCategorias.cate}', '${dbCategorias.acti}', "
              "'${dbCategorias.idim}', ${dbCategorias.uscr}, '${dbCategorias
              .fecr}', ${dbCategorias.usmo}, '${dbCategorias
              .femo}', ${dbCategorias.orde})"
      );
      return res;
    }on DatabaseException catch(e){
      return null;
    }
  }

  Future<int> newArchivosRaw(DbArchivos dbArchivos) async{
    final db = await database;
    final res = await db.rawInsert(
        "INSERT OR REPLACE INTO archivos (idar, esta, secc, cate, ides, idse, idca, arch, acti, arfi, idim, uscr, fecr, usel, feel, orde, femo, usmo, dinombre)"
            "VALUES (${dbArchivos.idar}, '${dbArchivos.esta}', '${dbArchivos.secc}', '${dbArchivos.cate}', ${dbArchivos.ides}, ${dbArchivos.idse}, "
            "${dbArchivos.idca}, '${dbArchivos.arch}', '${dbArchivos.acti}', '${dbArchivos.arfi}', '${dbArchivos.idim}', ${dbArchivos.uscr}, "
            "'${dbArchivos.fecr}', '${dbArchivos.usel}', '${dbArchivos.feel}', ${dbArchivos.orde}, '${dbArchivos.femo}', ${dbArchivos.usmo}, "
            "'${dbArchivos.dinombre}')"
    );
    return res;
  }

  Future<Map<String, dynamic>> getLoginInfo() async{
    final db = await database;
    final res = await db.query('login');
    if(res.isNotEmpty){
      int i = 0;
      Response response;
      DateTime fecha;
      String pass;
      do{
        response = Response(
            result: Result(
                accessToken: res[i]['accessToken'].toString(),
                dinum: res[i]['dinum'].toString(),
                user: res[i]['user'].toString(),
                role: res[i]['role'].toString(),
                lsec: res[i]['lsec'].toString()
            ),
            response: true,
            message: 'Login encontrado'
        );
        final strDate = res[i]['fecha'].toString().substring(0, 19);
        fecha = DateTime.parse(strDate);
        pass = res[i]['pass'].toString();
        i++;
      }while(i < res.length);
      return {
        'response' : response,
        'fecha' : fecha,
        'pass' : pass
      };
    }else{
      return {
        'response' : Response(
            result: Result(
                accessToken: 'no-data',
                dinum: 'no-data',
                user: 'no-data',
                role: 'no-data',
                lsec: 'no-data'
            ),
            response: false,
            message: 'No se ha encontrado ningún login anterior'
        ),
        'fecha' : DateTime.now(),
        'pass' : 'no-data'
      };
    }
  }

  Future<List<DbEstantes>> getAllEstantes() async{
    final db = await database;
    final res = await db.query('estantes', orderBy: 'orde');
    List<DbEstantes> list = [];
    if(res.isNotEmpty){
      int i = 0;
      do{
        final dbEstande = DbEstantes(
            ides: res[i]['ides'] as int,
            esta: res[i]['esta'].toString(),
            acti: res[i]['acti'].toString(),
            idim: res[i]['idim'].toString(),
            uscr: res[i]['uscr'] as int,
            fecr: res[i]['fecr'].toString(),
            usmo: res[i]['usmo'] as int,
            femo: res[i]['femo'].toString(),
            orde: res[i]['orde'] as int
        );
        i++;
        list.add(dbEstande);
      }while(i < res.length);
    }
    return list;
  }

  Future<List<DbEstantes>> getAllEstantesOrderedByDate() async{
    final db = await database;
    final res = await db.query('estantes', orderBy: 'femo');
    List<DbEstantes> list = [];
    if(res.isNotEmpty){
      int i = 0;
      do{
        final dbEstande = DbEstantes(
            ides: res[i]['ides'] as int,
            esta: res[i]['esta'].toString(),
            acti: res[i]['acti'].toString(),
            idim: res[i]['idim'].toString(),
            uscr: res[i]['uscr'] as int,
            fecr: res[i]['fecr'].toString(),
            usmo: res[i]['usmo'] as int,
            femo: res[i]['femo'].toString(),
            orde: res[i]['orde'] as int,
        );
        i++;
        list.add(dbEstande);
      }while(i < res.length);
    }
    return list;
  }

  Future<List<DbEstantes>> getAllEstantesByDate(String initDate, String finishDate) async{
    final db = await database;
    final res = await db.query('estantes', where: 'fecr BETWEEN ? AND ?', whereArgs: [initDate, finishDate], orderBy: 'fecr');
    List<DbEstantes> list = [];
    int i = 0;
    do{
      final dbEstande = DbEstantes(
          ides: res[i]['ides'] as int,
          esta: res[i]['esta'].toString(),
          acti: res[i]['acti'].toString(),
          idim: res[i]['idim'].toString(),
          uscr: res[i]['uscr'] as int,
          fecr: res[i]['fecr'].toString(),
          usmo: res[i]['usmo'] as int,
          femo: res[i]['femo'].toString(),
          orde: res[i]['orde'] as int
      );
      i++;
      list.add(dbEstande);
    }while(i < res.length);
    return list;
  }

  Future<List<DbSecciones>> getSections(int id) async{
    final db = await database;
    final res = await db.query('secciones', where: 'ides = ?', whereArgs: [id]);
    List<DbSecciones> list = [];
    if(res.isNotEmpty){
      int i = 0;
      do{
        final dbSecciones = DbSecciones(
            idse: res[i]['idse'] as int,
            esta: res[i]['esta'].toString(),
            ides: res[i]['ides'] as int,
            secc: res[i]['secc'].toString(),
            acti: res[i]['acti'].toString(),
            idim: res[i]['idim'].toString(),
            uscr: res[i]['uscr'] as int,
            fecr: res[i]['fecr'].toString(),
            usmo: res[i]['usmo'] as int,
            femo: res[i]['femo'].toString(),
            orde: res[i]['orde'] as int
        );
        i++;
        list.add(dbSecciones);
      }while(i < res.length);
    }
    return list == null ? [] : list;
  }

  Future<List<DbCategorias>> getCategories(int id) async{
    final db = await database;
    final res = await db.query('categorias', where: 'idse = ?', whereArgs: [id]);
    List<DbCategorias> list = [];
    if(res.isNotEmpty){
      int i = 0;
      do{
        final dbCategorias = DbCategorias(
            idca: res[i]['idca'] as int,
            secc: res[i]['secc'].toString(),
            esta: res[i]['esta'].toString(),
            idse: res[i]['idse'] as int,
            cate: res[i]['cate'].toString(),
            acti: res[i]['acti'].toString(),
            idim: res[i]['idim'].toString(),
            uscr: res[i]['uscr'] as int,
            fecr: res[i]['fecr'].toString(),
            usmo: res[i]['usmo'] as int,
            femo: res[i]['femo'].toString(),
            orde: res[i]['orde'] as int
        );
        i++;
        list.add(dbCategorias);
      }while(i < res.length);
    }
    return list == null ? [] : list;
  }

  Future<List<DbArchivos>> getFiles(int ides, int idse, int idca) async{
    final db = await database;
    final res = await db.query('archivos', where: 'ides = ? AND idse = ? AND idca = ?', whereArgs: [ides, idse, idca]);
    List<DbArchivos> list = [];
    if(res.isNotEmpty){
      int i = 0;
      do{
        final dbArchivo = DbArchivos(
            idar: res[i]['idar'] as int,
            esta: res[i]['esta'].toString(),
            secc: res[i]['secc'].toString(),
            cate: res[i]['cate'].toString(),
            ides: res[i]['ides'] as int,
            idse: res[i]['idse'] as int,
            idca: res[i]['idca'] as int,
            arch: res[i]['arch'].toString(),
            acti: res[i]['acti'].toString(),
            arfi: res[i]['arfi'].toString(),
            idim: res[i]['idim'].toString(),
            uscr: res[i]['uscr'] as int,
            fecr: res[i]['fecr'].toString(),
            usel: int.parse(res[i]['usel'].toString()),
            feel: res[i]['feel'].toString(),
            orde: res[i]['orde'] as int,
            femo: res[i]['femo'].toString(),
            usmo: res[i]['usmo'] as int,
            dinombre: res[i]['dinombre'].toString()
        );
        i++;
        list.add(dbArchivo);
      }while(i < res.length);
    }
    return list == null ? [] : list;
  }

  Future<int> deleteAllLogs() async{
    final db = await database;
    final res = await db.delete('login');
    return res;
  }

  Future<int> deleteAllEstantes() async{
    final db = await database;
    final res = await db.delete('estantes');
    return res;
  }

  Future<int> deleteAllSecciones() async{
    final db = await database;
    final res = await db.delete('secciones');
    return res;
  }

  Future<int> deleteAllCategorias() async{
    final db = await database;
    final res = await db.delete('categorias');
    return res;
  }

  Future<int> deleteAllArchivos() async{
    final db = await database;
    final res = await db.delete('archivos');
    return res;
  }

  String orderDate(String date){
    if(date != ''){
      String day = date.substring(0, 2);
      String month = date.substring(3, 6);
      String year = date.substring(7);
      String dbMonth = "";
      switch(month){
        case 'JAN':
          dbMonth = '01';
          break;
        case 'ENE':
          dbMonth = '01';
          break;
        case 'FEB':
          dbMonth = '02';
          break;
        case 'MAR':
          dbMonth = '03';
          break;
        case 'ABR':
          dbMonth = '04';
          break;
        case 'APR':
          dbMonth = '04';
          break;
        case 'MAY':
          dbMonth = '05';
          break;
        case 'JUN':
          dbMonth = '06';
          break;
        case 'JUL':
          dbMonth = '07';
          break;
        case 'AGO':
          dbMonth = '08';
          break;
        case 'AUG':
          dbMonth = '08';
          break;
        case 'SEP':
          dbMonth = '09';
          break;
        case 'OCT':
          dbMonth = '10';
          break;
        case 'NOV':
          dbMonth = '11';
          break;
        case 'DEC':
          dbMonth = '12';
          break;
        case 'DIC':
          dbMonth = '12';
          break;
        default:
          dbMonth = '00';
          break;
      }
      return '20$year-$dbMonth-$day';
    }else{
      return '';
    }
  }
}