package com.sifang.modules.sys.util;

import okhttp3.*;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 并发加载多个url。
 * <p>
 * author gaols
 */
public class AsyncLoader {

    private final OkHttpClient cli;
    private static final Logger logger = LoggerFactory.getLogger(AsyncLoader.class);
    private final AtomicInteger count = new AtomicInteger();
    private final AtomicInteger loadedCount = new AtomicInteger();
    private final File tmpSaveDir;
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition condition;

    private static final Logger log = LoggerFactory.getLogger(AsyncLoader.class);

    public AsyncLoader(OkHttpClient cli, File tmpSaveDir) {
        this.cli = cli;
        this.tmpSaveDir = tmpSaveDir;
        this.condition = lock.newCondition();
    }

    /**
     * 需要并发加载的附件。
     */
    public void load(String url, String label) {
        count.incrementAndGet();
        doLoad(url, label);
    }

    /**
     * 按照load调用的顺序，返回文件下载后对应的本地文件路径。警告：调用本方法后不应该再调用load方法。
     */
    public void awaitResult() throws InterruptedException {
        lock.lock();
        try {
            while (true) {
                if (loadedCount.get() != count.get()) {
                    condition.await();
                } else {
                    break;
                }
            }
        } finally {
            lock.unlock();
        }
    }

    private void doLoad(String url, String label) {
        CacheControl cacheControl = new CacheControl.Builder()
                .maxAge(3, TimeUnit.DAYS)
//                .noStore() // for test only
                .build();

        Request request = null;
        try {
            request = new Request.Builder()
                    .url(url)
                    .cacheControl(cacheControl)
                    .build();
        } catch (Exception ex) {
            lock.lock();
            try {
                loadedCount.incrementAndGet();
                condition.signal();
            } finally {
                lock.unlock();
            }
        }
        if (request == null) {
            return;
        }

        cli.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                try {
                    logger.error("load [{}] error", url, e);
                } finally {
                    lock.lock();
                    try {
                        loadedCount.incrementAndGet();
                        condition.signal();
                    } finally {
                        lock.unlock();
                    }
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                try (ResponseBody body = response.body()) {
                    if (!response.isSuccessful() || body == null) {
                        String errMsg = String.format("load [%s] error for response code is: %s", url, response.code());
                        if (body == null) {
                            errMsg += " and response body is null";
                        }
                        log.error("load external attachment error: {}", errMsg);
                    } else {
                        String extension = ".pdf";
                        FileUtils.writeByteArrayToFile(new File(tmpSaveDir, label + extension), body.bytes());
                    }
                } finally {
                    lock.lock();
                    try {
                        loadedCount.incrementAndGet();
                        condition.signal();
                    } finally {
                        lock.unlock();
                    }
                }
            }
        });
    }
}
