' ============================================================
' WALKGUIDE — DESIGN PATTERNS (GoF)
' Flutter × Spring Boot × In-Device AI
' Pattern 7 of 7: CHAIN OF RESPONSIBILITY (Behavioral)
' ============================================================

@startuml WalkGuide_07_ChainOfResponsibility

skinparam monochrome false
skinparam shadowing false
skinparam defaultFontName Arial
skinparam defaultFontSize 12
skinparam roundCorner 10
skinparam ArrowColor #555555
skinparam ArrowThickness 1.2

skinparam class {
    BackgroundColor #FAFAFA
    BorderColor #AAAAAA
    HeaderBackgroundColor #E8E8E8
    FontColor #222222
    StereotypeFontColor #666666
    AttributeFontColor #333333
}

skinparam package {
    BackgroundColor #F5F5F5
    BorderColor #888888
    FontColor #333333
    FontStyle bold
}

skinparam note {
    BackgroundColor #FFFDE7
    BorderColor #F9A825
    FontColor #444444
    FontSize 11
}

' ============================================================
' PATTERN 7 — CHAIN OF RESPONSIBILITY (Behavioral)
' ============================================================

package "⑦  Chain of Responsibility  [Behavioral]" #FCE4EC {

    ' --- Flutter: Dio Interceptor Chain ---
    abstract class "Interceptor\n<<Handler>>" as DioInterceptor {
        + onRequest(options, handler)
        + onResponse(response, handler)
        + onError(error, handler)
    }

    class "AuthInterceptor\n<<ConcreteHandler>>" as AuthInterceptor {
        - _secureStorage : FlutterSecureStorage
        + onRequest(options, handler)
        ' Attach Bearer token ke setiap request
        + onError(error, handler)
        ' Jika 401: refresh token → retry request
    }

    class "ErrorInterceptor\n<<ConcreteHandler>>" as ErrorInterceptor {
        + onError(error, handler)
        ' Map HTTP error codes → Failure domain objects
        ' 401 → UnauthorizedFailure
        ' 404 → NotFoundFailure
        ' 500 → ServerFailure
    }

    class "LogInterceptor\n<<ConcreteHandler>>" as LogInterceptorFlutter {
        + onRequest(options, handler)
        + onResponse(response, handler)
        + onError(error, handler)
        ' Print request/response untuk debugging
    }

    class "ApiClient\n<<Dio>>" as ApiClient {
        - _dio : Dio
        + get(url) : Response
        + post(url, data) : Response
        + put(url, data) : Response
        ' Interceptors dieksekusi berurutan
    }

    ' --- Backend: Spring Security Filter Chain ---
    abstract class "OncePerRequestFilter\n<<Handler>>" as SpringFilter {
        + {abstract} doFilterInternal(req, res, chain)
        + doFilter(req, res, chain)
    }

    class "JwtAuthFilter\n<<ConcreteHandler>>" as JwtAuthFilter {
        - _jwtUtil : JwtUtil
        - _userDetailsService : UserDetailsService
        + doFilterInternal(req, res, chain)
        ' Extract JWT → validate → set SecurityContext
        ' Jika invalid: 401 Unauthorized langsung
    }

    class "SecurityConfig\n<<Config/Handler>>" as SecurityConfigChain {
        + securityFilterChain(http) : SecurityFilterChain
        ' /auth/** → permitAll
        ' /api/v1/** → authenticated
        ' ROLE_GUARDIAN vs ROLE_USER routing
    }

    class "Controller\n<<Endpoint>>" as ControllerChain {
        + handleRequest(request) : ResponseEntity
        ' Request tiba hanya jika lolos semua filter
    }

    DioInterceptor <|-- AuthInterceptor : extends
    DioInterceptor <|-- ErrorInterceptor : extends
    DioInterceptor <|-- LogInterceptorFlutter : extends

    ApiClient --> AuthInterceptor : chain[0]
    AuthInterceptor --> ErrorInterceptor : passes to\nchain[1]
    ErrorInterceptor --> LogInterceptorFlutter : passes to\nchain[2]

    SpringFilter <|-- JwtAuthFilter : extends
    JwtAuthFilter --> SecurityConfigChain : passes to
    SecurityConfigChain --> ControllerChain : passes to\n(if authorized)
}

note bottom of ApiClient
  Flutter — Dio interceptor chain:
  HTTP Request → AuthInterceptor → ErrorInterceptor → LogInterceptor → Response
  Setiap handler bisa: proses, modifikasi, atau blok request.
  AuthInterceptor otomatis refresh JWT jika expired (401).
end note

note bottom of JwtAuthFilter
  Backend — Spring Security filter chain:
  HTTP Request → JwtAuthFilter → SecurityConfig → Controller
  JwtAuthFilter extract & validasi Bearer token setiap request.
  Jika invalid → langsung 401 tanpa lanjut ke controller.
end note

@enduml
