/*
 * Copyright 1999-2023 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.alibaba.nacos.naming.misc;

import com.alibaba.nacos.consistency.Serializer;
import com.alibaba.nacos.consistency.snapshot.LocalFileMeta;
import com.alibaba.nacos.consistency.snapshot.Reader;
import com.alibaba.nacos.consistency.snapshot.Writer;
import com.alibaba.nacos.naming.consistency.persistent.impl.AbstractSnapshotOperation;
import com.alibaba.nacos.sys.utils.DiskUtils;
import com.alipay.sofa.jraft.util.CRC64;

import java.nio.file.Paths;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.zip.Checksum;

/**
 * Switch Domain snapshot operation.
 *
 * @author xiweng.yy
 */
public class SwitchDomainSnapshotOperation extends AbstractSnapshotOperation {
    
    private final String snapshotDir = "naming_persistent";
    
    private final String snapshotArchive = "naming_persistent.zip";
    
    private final SwitchManager switchManager;
    
    private final Serializer serializer;
    
    public SwitchDomainSnapshotOperation(ReentrantReadWriteLock lock, SwitchManager switchManager,
            Serializer serializer) {
        super(lock);
        this.switchManager = switchManager;
        this.serializer = serializer;
    }
    
    @Override
    protected boolean writeSnapshot(Writer writer) throws Exception {
        final String writePath = writer.getPath();
        final String parentPath = Paths.get(writePath, snapshotDir).toString();
        DiskUtils.deleteDirectory(parentPath);
        DiskUtils.forceMkdir(parentPath);
        
        this.switchManager.dumpSnapshot(parentPath);
        final String outputFile = Paths.get(writePath, snapshotArchive).toString();
        final Checksum checksum = new CRC64();
        DiskUtils.compress(writePath, snapshotDir, outputFile, checksum);
        DiskUtils.deleteDirectory(parentPath);
        
        final LocalFileMeta meta = new LocalFileMeta();
        meta.append(CHECK_SUM_KEY, Long.toHexString(checksum.getValue()));
        return writer.addFile(snapshotArchive, meta);
    }
    
    @Override
    protected boolean readSnapshot(Reader reader) throws Exception {
        final String readerPath = reader.getPath();
        final String sourceFile = Paths.get(readerPath, snapshotArchive).toString();
        final Checksum checksum = new CRC64();
        DiskUtils.decompress(sourceFile, readerPath, checksum);
        LocalFileMeta fileMeta = reader.getFileMeta(snapshotArchive);
        if (fileMeta.getFileMeta().containsKey(CHECK_SUM_KEY)) {
            if (!Objects.equals(Long.toHexString(checksum.getValue()), fileMeta.get(CHECK_SUM_KEY))) {
                throw new IllegalArgumentException("Snapshot checksum failed");
            }
        }
        final String loadPath = Paths.get(readerPath, snapshotDir).toString();
        Loggers.RAFT.info("snapshot load from : {}", loadPath);
        this.switchManager.loadSnapshot(loadPath);
        DiskUtils.deleteDirectory(loadPath);
        return true;
    }
    
    @Override
    protected String getSnapshotSaveTag() {
        return SwitchDomainSnapshotOperation.class.getSimpleName() + ".SAVE";
    }
    
    @Override
    protected String getSnapshotLoadTag() {
        return SwitchDomainSnapshotOperation.class.getSimpleName() + ".LOAD";
    }
}
