/**
 * @athenna/database
 *
 * (c) João Lenon <lenon@athenna.io>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

import { relative } from 'node:path'
import { BaseConfigurer } from '@athenna/artisan'
import { File, Module, Parser, Path } from '@athenna/common'

export default class DatabaseConfigurer extends BaseConfigurer {
  public async configure() {
    const connection = await this.prompt.list(
      'What will be your default connection?',
      ['mysql', 'postgres', 'sqlite', 'mongo']
    )

    const ext = Path.ext()
    const task = this.logger.task()

    task.addPromise(`Create database.${ext} configuration file`, () => {
      return new File(`./database`).copy(Path.config(`database.${ext}`))
    })

    task.addPromise('Update commands of .athennarc.json', () => {
      return this.rc
        .setTo(
          'commands',
          'make:model',
          '@athenna/database/commands/MakeModelCommand'
        )
        .setTo(
          'commands',
          'make:seeder',
          '@athenna/database/commands/MakeSeederCommand'
        )
        .setTo(
          'commands',
          'make:migration',
          '@athenna/database/commands/MakeMigrationCommand'
        )
        .setTo('commands', 'make:crud', {
          path: '@athenna/database/commands/MakeCrudCommand',
          fileCase: 'toDotCase'
        })
        .setTo('commands', 'db:fresh', {
          path: '@athenna/database/commands/DbFreshCommand',
          loadApp: true,
          loadAllCommands: true
        })
        .setTo('commands', 'db:seed', {
          path: '@athenna/database/commands/DbSeedCommand',
          loadApp: true
        })
        .setTo('commands', 'db:wipe', {
          path: '@athenna/database/commands/DbWipeCommand',
          loadApp: true
        })
        .setTo('commands', 'migration:run', {
          path: '@athenna/database/commands/MigrationRunCommand',
          loadApp: true
        })
        .setTo('commands', 'migration:revert', {
          path: '@athenna/database/commands/MigrationRevertCommand',
          loadApp: true
        })
        .save()
    })

    task.addPromise('Update templates of .athennarc.json', () => {
      return this.rc
        .setTo(
          'templates',
          'model',
          'node_modules/@athenna/database/templates/model.edge'
        )
        .setTo(
          'templates',
          'seeder',
          'node_modules/@athenna/database/templates/seeder.edge'
        )
        .setTo(
          'templates',
          'migration',
          'node_modules/@athenna/database/templates/migration.edge'
        )
        .setTo(
          'templates',
          'crud-model',
          'node_modules/@athenna/database/templates/crud-model.edge'
        )
        .setTo(
          'templates',
          'crud-migration',
          'node_modules/@athenna/database/templates/crud-migration.edge'
        )
        .setTo(
          'templates',
          'crud-service',
          'node_modules/@athenna/database/templates/crud-service.edge'
        )
        .setTo(
          'templates',
          'crud-controller',
          'node_modules/@athenna/database/templates/crud-controller.edge'
        )
        .setTo(
          'templates',
          'crud-service-test',
          'node_modules/@athenna/database/templates/crud-service-test.edge'
        )
        .setTo(
          'templates',
          'crud-controller-test',
          'node_modules/@athenna/database/templates/crud-controller-test.edge'
        )
        .save()
    })

    task.addPromise('Update providers of .athennarc.json', () => {
      return this.rc
        .pushTo('providers', '@athenna/database/providers/DatabaseProvider')
        .save()
    })

    task.addPromise('Update .env, .env.test and .env.example', () => {
      let envs = ''

      switch (connection) {
        case 'mongo':
          envs =
            '\nDB_CONNECTION=mongo\n' +
            'DB_DEBUG=false\n' +
            'DB_URL=mongodb://root:root@localhost:27017/admin\n'
          break
        case 'sqlite':
          envs =
            '\nDB_CONNECTION=sqlite\n' +
            'DB_DEBUG=false\n' +
            `DB_FILENAME=${this.databasePath()}/sqlite.db\n`
          break
        default:
          // eslint-disable-next-line no-case-declarations
          const ports = {
            mysql: 3306,
            postgres: 5432
          }

          envs =
            `\nDB_CONNECTION=${connection}\n` +
            'DB_HOST=localhost\n' +
            `DB_PORT=${ports[connection]}\n` +
            'DB_DEBUG=false\n' +
            'DB_USERNAME=root\n' +
            'DB_PASSWORD=root\n' +
            'DB_DATABASE=athenna\n'
      }

      const testEnvs = envs.replace(
        `DB_CONNECTION=${connection}`,
        'DB_CONNECTION=fake'
      )

      return new File(Path.pwd('.env'), '')
        .append(envs)
        .then(() => new File(Path.pwd('.env.test'), '').append(testEnvs))
        .then(() => new File(Path.pwd('.env.example'), '').append(envs))
    })

    if (connection !== 'sqlite') {
      task.addPromise('Add service to docker-compose.yml file', async () => {
        const hasDockerCompose = await File.exists(
          Path.pwd('docker-compose.yml')
        )

        if (hasDockerCompose) {
          const docker = await new File(
            Path.pwd('docker-compose.yml')
          ).getContentAsYaml()

          docker.services[connection] = await Module.get(
            import(`./docker/${connection}/service.ts`)
          )

          return new File(Path.pwd('docker-compose.yml')).setContent(
            Parser.objectToYamlString(docker)
          )
        }

        return new File(`./docker/${connection}/file.yml`).copy(
          Path.pwd('docker-compose.yml')
        )
      })
    }

    const libraries = {
      mysql: ['knex', 'mysql2'],
      postgres: ['knex', 'pg'],
      sqlite: ['knex', 'better-sqlite3'],
      mongo: ['mongoose']
    }

    task.addPromise(
      `Install ${libraries[connection].join(', ')} libraries`,
      () => {
        return this.npm.install(libraries[connection])
      }
    )

    await task.run()

    console.log()
    this.logger.success(
      'Successfully configured ({dim,yellow} @athenna/database) library'
    )

    if (connection !== 'sqlite') {
      this.logger
        .instruction()
        .head('Run following commands to get started:')
        .add(`docker-compose up -d`)
        .render()
    }
  }

  private databasePath() {
    return relative(Path.pwd(), Path.database()).replace(/\\/g, '/')
  }
}
