import {defineStore} from "pinia";
import {reactive, toRefs, watch} from "vue";

import {request_crate} from "@/store/http/requests";
import queries from "@/store/http/queries";
import {Schemas} from "@/store/crate_api/tables";
import {Columns} from "@/store/crate_api/columns";
import {use_global_store} from "@/store/global_store";
import {use_log_store} from "@/store/log";

export const use_tables_store = defineStore('tables', () => {
    const state = reactive({
        schemas: new Schemas([]),
        current_tab: '',
        current_open_table: null,
        current_open_schema: null, // We only need this to have a Schema reference.
        current_open_table_columns: null,
        current_show_create_table: null,
        sample_data: null,
        response_from_create_table: {},
        current_last_inserted_ms: null,

        drawer_opened_tabs: []
        // Keeps tracks of the tabs that are opened in the tables drawer, for example ['doc', 'doc.tables'] would be the state
        // if the user had the doc schema opened, and the tables of the doc schema.
    })
    const log_store = use_log_store()
    const global_store = use_global_store()

    async function update_tables() {
        const _response = await request_crate(queries.TABLES)
        const tables = await _response.json()
        state.schemas = new Schemas(tables.rows)
    }

    async function update_table_columns_information(table_name, table_schema) {
        // Gets the columns for the current open table.
        const _response = await request_crate(queries.COLUMNS, null, {
            '%table_name': table_name,
            '%table_schema': table_schema
        })
        const columns = await _response.json()
        state.current_open_table_columns = new Columns(columns.rows)
    }

    async function update_table_sample_data() {
        // Reset just in case there is data from previous calls
        state.sample_data = null

        const response = await request_crate(queries.SAMPLE_DATA, null, {
            '%table_name': state.current_open_table.name,
            '%table_schema': state.current_open_table.schema
        })
        const data = await response.json()
        if (response.ok) {
            state.sample_data = data
        } else {
            state.sample_data = {
                cols: [],
                rows: []
            }
        }
    }

    async function drop_table() {
        const query = state.current_open_table.is_view() ? queries.DROP_VIEW : queries.DROP_TABLE
        const response = await request_crate(query,
            null,
            {
                '%schema_name': state.current_open_table.schema,
                '%table_name': state.current_open_table.name
            })

        if (response.ok) {
            const _table_fqdn = state.current_open_table.schema + '.' + state.current_open_table.name
            state.schemas.remove_table(state.current_open_table)
            // Perhaps we can locally delete the data instead of forcing a request + re-drawn.
            // this costs a few milliseconds delay when deleting a table, it is not a 'problem'
            // but it is noticeable, should be fixed to ensure max smoothness.
            // We do it correctly in drop user and table privileges for reference.
            state.current_open_table = null // Redraws the card, in this case, removing it.
            await log_store.log(log_store.ACTIONS.DROP_TABLE, _table_fqdn)
        }
        return response
    }

    async function create_table(sql) {
        // In this particular case, the sql is generated by the UI in Tables.vue
        const response = await request_crate(sql)
        if (response.ok) {
            // We don't manipulate the local data here manually because it's
            // too cumbersome.
            await update_tables()
        }
        return response
    }

    async function rename_table(new_name) {

      const response = await request_crate(queries.RENAME_TABLE, null, {
        '%schema': state.current_open_table.schema,
        '%old_table': state.current_open_table.name,
        '%new_table': new_name,
      })
      if (response.ok) {
        state.current_open_table.name = new_name
      }
      return response
    }

    async function show_create_table() {
        const response = await request_crate(
            queries.SHOW_CREATE,
            null,
            {
                '%table_schema': state.current_open_table.schema,
                '%table_name': state.current_open_table.name
            }
        )
        const data = await response.json()
        state.current_show_create_table = data.rows[0][0]
    }

    async function get_last_insert(table_schema, table_name) {
      const response = await request_crate(
        queries.TABLE_LAST_INSERT,
        null,
        {
          '%table_schema': table_name,
          '%table_name': table_schema
        }
      )

      const data = await response.json()

      if (data.rows.length > 0) {
        state.current_last_inserted_ms = data.rows[0][0]
      } else {
        state.current_last_inserted_ms = null
      }
    }

    watch(
        () => state.current_open_table, async (value) => {
        if (value == null) {
          // We check for null because this could get triggered in situations where for example:
          // state.current_open_table is 'doc.table_test', we DROP the table, we set
          // state.current_open_table to null, resetting the entire view, closing the table card
          // and updating the left drawer, in that situation, we do not want this to be called
          // since there is no table to get the columns for, nor the need.
          return
        }
        await update_table_columns_information(value.name, value.schema)

        if (!state.current_open_table.is_view() && !state.current_open_schema.is_system) {
          await get_last_insert(value.name, value.schema)
        }

        // If we change from one table to another, reset the tab position, this is intended
        // to avoid unnecessary calls, for example if the user is in 'doc.table' and has
        // the sample_table tab selected, if he changes to another table, it is preferable
        // not to open the table in the same tab, but reset it to the column's, if the
        // user wants again sample_table, for example, he will have to click once again.
        state.sample_data = null
        state.current_tab = 'one'
        }
    )
    return {
      ...toRefs(state),
      create_table,
      update_tables,
      update_table_sample_data,
      drop_table,
      show_create_table,
      rename_table,
      get_last_insert
    }
})
