package ch.threema.storage.factories;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import net.zetetic.database.sqlcipher.SQLiteDatabase;

import org.slf4j.Logger;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static ch.threema.base.utils.LoggingKt.getThreemaLogger;
import ch.threema.storage.CursorHelper;
import ch.threema.storage.DatabaseCreationProvider;
import ch.threema.storage.DatabaseProvider;
import ch.threema.storage.models.group.GroupCallModel;

public class GroupCallModelFactory extends ModelFactory {
    private static final Logger logger = getThreemaLogger("GroupCallModelFactory");

    public GroupCallModelFactory(DatabaseProvider databaseProvider) {
        super(databaseProvider, GroupCallModel.TABLE);
    }

    @NonNull
    public List<GroupCallModel> getAll() {
        String[] columns = new String[]{
            GroupCallModel.COLUMN_CALL_ID,
            GroupCallModel.COLUMN_GROUP_ID,
            GroupCallModel.COLUMN_SFU_BASE_URL,
            GroupCallModel.COLUMN_GCK,
            GroupCallModel.COLUMN_PROTOCOL_VERSION,
            GroupCallModel.COLUMN_STARTED_AT,
            GroupCallModel.COLUMN_PROCESSED_AT,
        };
        Cursor cursor = getReadableDatabase()
            .query(getTableName(), columns, null, null, null, null, null);
        try (cursor) {
            List<GroupCallModel> calls = convertList(cursor);
            logger.debug("Get {} calls from database", calls.size());
            return calls;
        } catch (SQLiteException e) {
            logger.error("Error while getting group call models", e);
            return Collections.emptyList();
        }
    }

    public void createOrUpdate(GroupCallModel call) {
        ContentValues contentValues = new ContentValues();
        contentValues.put(GroupCallModel.COLUMN_CALL_ID, call.getCallId());
        contentValues.put(GroupCallModel.COLUMN_GROUP_ID, call.getGroupId());
        contentValues.put(GroupCallModel.COLUMN_SFU_BASE_URL, call.getSfuBaseUrl());
        contentValues.put(GroupCallModel.COLUMN_GCK, call.getGck());
        contentValues.put(GroupCallModel.COLUMN_PROTOCOL_VERSION, call.getProtocolVersion());
        contentValues.put(GroupCallModel.COLUMN_STARTED_AT, call.getStartedAt());
        contentValues.put(GroupCallModel.COLUMN_PROCESSED_AT, call.getProcessedAt());

        try {
            long id = getWritableDatabase()
                .insertWithOnConflict(getTableName(), null, contentValues, SQLiteDatabase.CONFLICT_REPLACE);
            logger.debug("Insert or update call: {}", id);
        } catch (SQLiteException e) {
            logger.error("Could not create or update call", e);
        }
    }

    public void delete(GroupCallModel call) {
        try {
            int count = getWritableDatabase().delete(
                getTableName(),
                GroupCallModel.COLUMN_CALL_ID + "=?",
                new String[]{call.getCallId()}
            );
            logger.debug("Delete call with id {}: {}", call.getCallId(), count);
        } catch (SQLiteException e) {
            logger.error("Could not delete call", e);
        }
    }

    @NonNull
    private List<GroupCallModel> convertList(Cursor cursor) {
        List<GroupCallModel> calls = new ArrayList<>();
        if (cursor != null) {
            while (cursor.moveToNext()) {
                GroupCallModel call = convert(cursor);
                if (call != null) {
                    calls.add(call);
                }
            }
        }
        return calls;
    }

    @Nullable
    private GroupCallModel convert(@NonNull Cursor cursor) {
        CursorHelper.CallbackInstance<GroupCallModel> converter = cursorHelper -> {
            Integer protocolVersion = cursorHelper.getInt(GroupCallModel.COLUMN_PROTOCOL_VERSION);
            String callId = cursorHelper.getString(GroupCallModel.COLUMN_CALL_ID);
            Integer groupId = cursorHelper.getInt(GroupCallModel.COLUMN_GROUP_ID);
            String baseUrl = cursorHelper.getString(GroupCallModel.COLUMN_SFU_BASE_URL);
            String gck = cursorHelper.getString(GroupCallModel.COLUMN_GCK);
            Long startedAt = cursorHelper.getLong(GroupCallModel.COLUMN_STARTED_AT);
            Long processedAt = cursorHelper.getLong(GroupCallModel.COLUMN_PROCESSED_AT);
            if (startedAt == null) {
                startedAt = Instant.now().toEpochMilli();
            }
            if (processedAt == null) {
                processedAt = Instant.now().toEpochMilli();
            }

            return protocolVersion == null || groupId == null || baseUrl == null || callId == null || gck == null
                ? null
                : new GroupCallModel(protocolVersion, callId, groupId, baseUrl, gck, startedAt, processedAt);
        };
        return new CursorHelper(cursor, getColumnIndexCache()).current(converter);
    }

    public static class Creator implements DatabaseCreationProvider {
        @Override
        @NonNull
        public String [] getCreationStatements() {
            return new String[]{
                "CREATE TABLE `" + GroupCallModel.TABLE + "` (" +
                    "`" + GroupCallModel.COLUMN_CALL_ID + "` TEXT PRIMARY KEY NOT NULL, " +
                    "`" + GroupCallModel.COLUMN_GROUP_ID + "` INTEGER NOT NULL, " +
                    "`" + GroupCallModel.COLUMN_SFU_BASE_URL + "` TEXT NOT NULL, " +
                    "`" + GroupCallModel.COLUMN_GCK + "` TEXT NOT NULL, " +
                    "`" + GroupCallModel.COLUMN_PROTOCOL_VERSION + "` INTEGER DEFAULT 0," +
                    "`" + GroupCallModel.COLUMN_STARTED_AT + "` BIGINT NOT NULL," +
                    "`" + GroupCallModel.COLUMN_PROCESSED_AT + "` BIGINT NOT NULL)"
            };
        }
    }
}
