# DeepLabV3+ Model
import tensorflow as tf
from keras.layers import Input
from keras.layers import Conv2D
from keras.layers import Concatenate
from keras.layers import UpSampling2D
from keras.models import Model
from tensorflow.keras.applications import ResNet50

from .config import IMAGE_SIZE, LEARNING_RATE, LLF_FILTERS, RESNET_WEIGHTS
from .layers import ConvBlock, AtrousSpatialPyramidPooling


def create_model(image_size=IMAGE_SIZE, learning_rate=LEARNING_RATE,
                 compile_model=True, in_channels=4):
    """
    DeepLabV3+ 모델 생성

    Args:
        image_size: 입력 이미지 크기
        learning_rate: 학습률
        compile_model: True면 모델 컴파일 포함
        in_channels: 입력 채널 수 (4 = R,G,B,MNDWI)

    Returns:
        DeepLabV3+ Keras Model
    """
    # Input Layer
    InputL = Input(shape=(image_size, image_size, in_channels), name="InputLayer")

    # 4채널 → 3채널 변환 (ResNet50은 3채널만 지원)
    if in_channels != 3:
        x = Conv2D(3, kernel_size=1, padding='same', name="ChannelReduce")(InputL)
    else:
        x = InputL

    # Backbone: ResNet50 (pretrained on ImageNet)
    # 별도 input_shape로 생성 후 중간 레이어 출력을 추출
    resnet50_base = ResNet50(
        include_top=False,
        weights=RESNET_WEIGHTS,
        input_shape=(image_size, image_size, 3)
    )
    # ResNet50의 중간 출력을 가져오기 위한 멀티출력 모델 생성
    resnet50 = Model(
        inputs=resnet50_base.input,
        outputs={
            'deep': resnet50_base.get_layer('conv4_block6_2_relu').output,
            'low': resnet50_base.get_layer('conv2_block3_2_relu').output,
        },
        name="ResNet50-Backbone"
    )
    features_dict = resnet50(x)

    # ASPP Phase - Deep CNN features
    DCNN = features_dict['deep']
    ASPP = AtrousSpatialPyramidPooling(DCNN)
    ASPP = UpSampling2D(
        size=(image_size // 4 // ASPP.shape[1], image_size // 4 // ASPP.shape[2]),
        name="AtrousSpatial"
    )(ASPP)

    # Low-Level Features (LLF) Phase
    LLF = features_dict['low']
    LLF = ConvBlock(filters=LLF_FILTERS, kernel_size=1, name="LLF-ConvBlock")(LLF)

    # Decoder: Combine ASPP and LLF
    combined = Concatenate(axis=-1, name="Combine-LLF-ASPP")([ASPP, LLF])
    features = ConvBlock(name="Top-ConvBlock-1")(combined)
    features = ConvBlock(name="Top-ConvBlock-2")(features)

    # Upsample to original size
    upsample = UpSampling2D(
        size=(image_size // features.shape[1], image_size // features.shape[1]),
        interpolation='bilinear',
        name="Top-UpSample"
    )(features)

    # Output Mask (1 channel for binary segmentation)
    PredMask = Conv2D(
        1,
        kernel_size=3,
        strides=1,
        padding='same',
        activation='sigmoid',
        use_bias=False,
        name="OutputMask"
    )(upsample)

    # Build Model
    model = Model(InputL, PredMask, name="DeepLabV3-Plus")

    # Compile if requested
    if compile_model:
        optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate)
        model.compile(loss='binary_crossentropy', optimizer=optimizer)

    return model


def load_model(model_path):
    """
    저장된 모델 로드

    Args:
        model_path: 모델 파일 경로 (.h5)

    Returns:
        로드된 Keras Model
    """
    from keras.models import load_model as keras_load_model
    from .layers import ConvBlock

    custom_objects = {
        'ConvBlock': ConvBlock
    }

    return keras_load_model(model_path, custom_objects=custom_objects)
