package lee.study.down.intercept.common;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.DefaultLastHttpContent;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpContentDecompressor;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.util.ReferenceCountUtil;
import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.util.zip.GZIPOutputStream;
import lee.study.down.util.ByteUtil;
import lee.study.proxyee.intercept.HttpProxyIntercept;
import lee.study.proxyee.intercept.HttpProxyInterceptPipeline;

public abstract class ResponseTextIntercept extends HttpProxyIntercept {

  private boolean isMatch = false;
  private boolean isGzip = false;
  private ByteBuf contentBuf;

  @Override
  public void afterResponse(Channel clientChannel, Channel proxyChannel, HttpResponse httpResponse,
      HttpProxyInterceptPipeline pipeline) throws Exception {
    if (match(httpResponse, pipeline)) {
      isMatch = true;
      //解压gzip响应
      if ("gzip".equalsIgnoreCase(httpResponse.headers().get(HttpHeaderNames.CONTENT_ENCODING))) {
        isGzip = true;
        pipeline.reset3();
        proxyChannel.pipeline().addAfter("httpCodec", "decompress", new HttpContentDecompressor());
        proxyChannel.pipeline().fireChannelRead(httpResponse);
      } else {
        if (isGzip) {
          httpResponse.headers().set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
        }
        contentBuf = PooledByteBufAllocator.DEFAULT.buffer();
      }
      //直接调用默认拦截器,跳过下载拦截器
      pipeline.getDefault()
          .afterResponse(clientChannel, proxyChannel, httpResponse, pipeline);
    } else {
      isMatch = false;
      pipeline.afterResponse(clientChannel, proxyChannel, httpResponse);
    }
  }

  @Override
  public void afterResponse(Channel clientChannel, Channel proxyChannel, HttpContent httpContent,
      HttpProxyInterceptPipeline pipeline) throws Exception {
    if (isMatch) {
      try {
        contentBuf.writeBytes(httpContent.content());
        if (httpContent instanceof LastHttpContent) {
          ByteUtil.insertText(contentBuf, ByteUtil.findText(contentBuf, "<head>"), hookResponse(),
              Charset.forName("UTF-8"));
          HttpContent hookHttpContent = new DefaultLastHttpContent();
          if (isGzip) { //转化成gzip编码
            byte[] temp = new byte[contentBuf.readableBytes()];
            contentBuf.readBytes(temp);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            GZIPOutputStream outputStream = new GZIPOutputStream(baos);
            outputStream.write(temp);
            outputStream.finish();
            hookHttpContent.content().writeBytes(baos.toByteArray());
          } else {
            hookHttpContent.content().writeBytes(contentBuf);
          }
          ReferenceCountUtil.release(contentBuf);
          pipeline.getDefault()
              .afterResponse(clientChannel, proxyChannel, hookHttpContent, pipeline);
        }
      } finally {
        ReferenceCountUtil.release(httpContent);
      }
    } else {
      pipeline.afterResponse(clientChannel, proxyChannel, httpContent);
    }
  }

  protected boolean isHtml(HttpResponse httpResponse, HttpProxyInterceptPipeline pipeline) {
    String accept = pipeline.getHttpRequest().headers().get(HttpHeaderNames.ACCEPT);
    String contentType = httpResponse.headers().get(HttpHeaderNames.CONTENT_TYPE);
    return accept != null && accept.matches("^.*text/html.*$") && contentType != null && contentType
        .matches("^text/html.*$");
  }

  public abstract boolean match(HttpResponse httpResponse, HttpProxyInterceptPipeline pipeline);

  public abstract String hookResponse();
}