import * as SQLite from 'expo-sqlite';
import * as FileSystem from 'expo-file-system';
import { Asset } from 'expo-asset';
import { API_HOST } from 'src/constants';
import { storage } from 'src/storage';
import { fetchLastDareDbUpdate, fetchLastRegionsDbUpdate } from '@api/app';

let db1: SQLite.SQLiteDatabase | null = null;
let db2: SQLite.SQLiteDatabase | null = null;
const nmRegionsDBname = 'nmRegions.db';
const darePlacesDBname = 'darePlaces.db';
const sqliteDirectory = 'SQLite';
const sqliteFullPath = FileSystem.documentDirectory + sqliteDirectory;
const DS = '/';

async function copyDatabaseFile(dbName: string, dbAsset: Asset) {
  console.log('DB copy start - ' + dbName);
  await dbAsset.downloadAsync();
  await FileSystem.downloadAsync(dbAsset.uri, sqliteFullPath + DS + dbName);

  const dbUri = sqliteFullPath + DS + dbName;

  await FileSystem.copyAsync({
    from: dbAsset.localUri ?? '',
    to: dbUri
  });
  return dbUri;
}

export async function openDatabases() {
  try {
    const fileInfo = await FileSystem.getInfoAsync(sqliteFullPath);
    if (!fileInfo.exists) {
      await FileSystem.makeDirectoryAsync(sqliteFullPath, { intermediates: true });
    }

    const nmRegionsDB = await FileSystem.getInfoAsync(sqliteFullPath + DS + nmRegionsDBname, {
      size: true
    });
    if (!nmRegionsDB.exists) {
      await copyDatabaseFile(
        nmRegionsDBname,
        Asset.fromModule(require('../../assets/db/' + nmRegionsDBname))
      );
    }
    if (nmRegionsDB.exists && nmRegionsDB.size == 0) {
      await FileSystem.deleteAsync(sqliteFullPath + DS + nmRegionsDBname);
      await copyDatabaseFile(
        nmRegionsDBname,
        Asset.fromModule(require('../../assets/db/' + nmRegionsDBname))
      );
    }

    const darePlacesDB = await FileSystem.getInfoAsync(sqliteFullPath + DS + darePlacesDBname, {
      size: true
    });
    if (!darePlacesDB.exists) {
      await copyDatabaseFile(
        darePlacesDBname,
        Asset.fromModule(require('../../assets/db/' + darePlacesDBname))
      );
    }

    if (darePlacesDB.exists && darePlacesDB.size == 0) {
      await FileSystem.deleteAsync(sqliteFullPath + DS + darePlacesDBname);
      await copyDatabaseFile(
        darePlacesDBname,
        Asset.fromModule(require('../../assets/db/' + darePlacesDBname))
      );
    }

    const openDatabase = (dbName: string) => SQLite.openDatabase(dbName);
    db1 = openDatabase(nmRegionsDBname);
    db2 = openDatabase(darePlacesDBname);
  } catch (error) {
    console.error('openDatabases - Error:');
    console.error(JSON.stringify(error, null, 2));
  }
}

export async function refreshDatabases() {
  try {
    const fileInfo = await FileSystem.getInfoAsync(sqliteFullPath);
    if (!fileInfo.exists) {
      await FileSystem.makeDirectoryAsync(sqliteFullPath, { intermediates: true });
    }
    await refreshNmDatabase();
    await refreshDarePlacesDatabase();
  } catch (error) {
    console.error('refreshDatabases - Error:');
    console.error(JSON.stringify(error, null, 2));
  }
}

export function getFirstDatabase() {
  return db1;
}

export function getSecondDatabase() {
  return db2;
}

const openDatabase = (dbName: string) => SQLite.openDatabase(dbName);

async function refreshNmDatabase() {
  try {
    await FileSystem.deleteAsync(sqliteFullPath + DS + nmRegionsDBname, { idempotent: true });
    const nmUrl = `${API_HOST}/static/app/${nmRegionsDBname}`;
    let nmFileUri = sqliteFullPath + DS + nmRegionsDBname;

    const nmResponse = await FileSystem.downloadAsync(nmUrl, nmFileUri);

    if (nmResponse.status !== 200) {
      throw new Error(`Failed to download the nmDb file: Status code ${nmResponse.status}`);
    }

    db1 = null;
    db1 = openDatabase(nmRegionsDBname);
  } catch (error) {
    console.error('refreshDatabase nmRegions - Error:');
    console.error(JSON.stringify(error, null, 2));
  }
}

async function refreshDarePlacesDatabase() {
  try {
    await FileSystem.deleteAsync(sqliteFullPath + DS + darePlacesDBname, { idempotent: true });
    const dareUrl = `${API_HOST}/static/app/${darePlacesDBname}`;
    let dareFileUri = sqliteFullPath + DS + darePlacesDBname;

    const dareResponse = await FileSystem.downloadAsync(dareUrl, dareFileUri);

    if (dareResponse.status !== 200) {
      throw new Error(`Failed to download the dareDb file: Status code ${dareResponse.status}`);
    }

    db2 = null;
    db2 = openDatabase(darePlacesDBname);
  } catch (error) {
    console.error('refreshDatabase darePlaces - Error:');
    console.error(JSON.stringify(error, null, 2));
  }
}

export async function updateNmRegionsDb(localLastDate: string) {
  const lastUpdate = await fetchLastRegionsDbUpdate(localLastDate);

  if (lastUpdate && lastUpdate.date !== localLastDate) {
    const dirInfo = await FileSystem.getInfoAsync(sqliteFullPath);

    if (!dirInfo.exists) {
      await FileSystem.makeDirectoryAsync(sqliteFullPath, { intermediates: true });
    }

    await refreshNmDatabase();
    storage.set('lastUpdateNmRegions', lastUpdate.date);
  }
}

export async function updateDarePlacesDb(localLastDate: string) {
  const lastUpdate = await fetchLastDareDbUpdate(localLastDate);

  if (lastUpdate && lastUpdate.date !== localLastDate) {
    const dirInfo = await FileSystem.getInfoAsync(sqliteFullPath);

    if (!dirInfo.exists) {
      await FileSystem.makeDirectoryAsync(sqliteFullPath, { intermediates: true });
    }

    await refreshDarePlacesDatabase();
    storage.set('lastUpdateDarePlaces', lastUpdate.date);
  }
}
