<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="kr.itn.itnhub.dashboard.DashboardMapper">

  <!--
    record라서 constructor 매핑을 쓴다(ProcessItemMapper의 statsResultMap과 같은 방식).
    arg 순서는 DashboardOrgRow의 컴포넌트 순서와 반드시 같아야 한다.
  -->
  <resultMap id="orgRowResultMap" type="kr.itn.itnhub.dashboard.DashboardOrgRow">
    <constructor>
      <idArg column="id" javaType="java.lang.Long"/>
      <arg column="org_no" javaType="java.lang.String"/>
      <arg column="org_name" javaType="java.lang.String"/>
      <arg column="stage" javaType="java.lang.Integer"/>
      <arg column="has_channel" javaType="_boolean"/>
      <arg column="lawyer_name" javaType="java.lang.String"/>
      <arg column="mj_name" javaType="java.lang.String"/>
      <arg column="lawyer_assigned_date" javaType="java.lang.String"/>
      <arg column="review_total" javaType="_int"/>
      <arg column="review_done" javaType="_int"/>
      <arg column="process_total" javaType="_int"/>
      <arg column="process_done" javaType="_int"/>
      <arg column="stage_changed_at" javaType="java.lang.Long"/>
      <arg column="last_changed_at" javaType="java.lang.Long"/>
      <arg column="re_count" javaType="_int"/>
    </constructor>
  </resultMap>

  <!--
    기관 1행당 집계를 서브쿼리로 붙인다. 기관별로 countByOrg/stats를 반복 호출하면
    기관 50곳 × 3종 = 150회 조회가 되므로(대시보드는 화면 진입마다 그린다) 여기서 한 방에 끝낸다.

    stage_at / any_at 두 값이 서로 다르다:
      - stage_at = 지금 단계로 넘어온 시각. 카드의 D+n은 이걸 쓴다
      - any_at   = 단계 이력 전체의 최신값. "최근 변경"의 재료 중 하나다
    lateral을 쓰는 이유는 filter 절에서 o.stage를 참조해야 하기 때문이다.

    last_changed_at은 greatest()로 합친다. Postgres의 greatest는 NULL을 무시하므로
    권리확인/처리/메모가 아직 없는 기관도 organization.updated_at으로 값이 채워진다.
    organization.updated_at만으로는 부족한 이유: 배정·단계·채널 변경 때만 갱신되고
    권리확인 업로드나 업무메모 작성은 이 컬럼을 건드리지 않는다.

    re_count는 아직 해결하지 않은 RE 메모 건수다(요구사항 [4]: 메모 1개 = RE 1개).
  -->
  <select id="findOrgRows" resultMap="orgRowResultMap">
    select
      o.id,
      o.org_no,
      o.org_name,
      o.stage,
      (o.channel_id_mj is not null or o.channel_id_law is not null) as has_channel,
      lc.name as lawyer_name,
      mc.name as mj_name,
      o.lawyer_assigned_date,
      coalesce(rv.total, 0) as review_total,
      coalesce(rv.done, 0)  as review_done,
      coalesce(pr.total, 0) as process_total,
      coalesce(pr.done, 0)  as process_done,
      (extract(epoch from sh.stage_at) * 1000)::bigint as stage_changed_at,
      (extract(epoch from greatest(o.updated_at, sh.any_at, rv.last_at, pr.last_at, wm.last_at)) * 1000)::bigint
        as last_changed_at,
      coalesce(re.unresolved, 0) as re_count
    from organization o
    left join (
      select org_id, count(*) as unresolved
      from re_memo where not resolved group by org_id
    ) re on re.org_id = o.id
    left join contact lc on lc.id = o.lawyer_contact_id
    left join contact mc on mc.id = o.mj_contact_id
    left join (
      select org_id,
             count(*) as total,
             count(*) filter (where review_result is not null and review_result &lt;&gt; '') as done,
             max(updated_at) as last_at
      from review_item group by org_id
    ) rv on rv.org_id = o.id
    left join (
      select org_id,
             count(*) as total,
             count(*) filter (where process_status = #{doneStatus}) as done,
             max(updated_at) as last_at
      from process_item group by org_id
    ) pr on pr.org_id = o.id
    left join (
      select org_id, max(created_at) as last_at from work_memo group by org_id
    ) wm on wm.org_id = o.id
    left join lateral (
      select max(h.changed_at) filter (where h.stage = o.stage) as stage_at,
             max(h.changed_at) as any_at
      from stage_history h
      where h.org_id = o.id
    ) sh on true
    order by o.org_no asc, o.org_name asc
  </select>

</mapper>
