import { EntityManager } from 'typeorm'

import { User } from '@things-factory/auth-base'
import { Bizplace } from '@things-factory/biz-base'
import { ListParam } from '@things-factory/shell'

import { rawProductsQuery } from '../../../utils'

export const costingReport = {
  async costingReport(_: any, params: ListParam, context: any) {
    try {
      const { tx }: { tx: EntityManager } = context.state
      let bizplaceFilter = { name: '', operator: '', value: '' }
      let userFilter = params.filters.find(data => data.name === 'user')
      let fromDate = params.filters.find(data => data.name === 'fromDate')
      let toDate = params.filters.find(data => data.name === 'toDate')

      if (userFilter) {
        const user: User = await tx.getRepository(User).findOne({
          where: { id: userFilter.value },
          relations: ['bizplaces']
        })

        const bizplace: Bizplace = user.bizplaces[0]

        if (!bizplace) throw 'Invalid input'

        bizplaceFilter = { name: 'bizplace', operator: 'eq', value: bizplace.id }
      } else {
        bizplaceFilter = params.filters.find(data => data.name === 'bizplace')
      }

      if (!bizplaceFilter || !fromDate || !toDate) throw 'Invalid input'

      const bizplace: Bizplace = await tx.getRepository(Bizplace).findOne({
        id: bizplaceFilter.value
      })

      await rawProductsQuery(tx, params, bizplace)

      await tx.query(
        `
        create temp table tmp_outbound_ord on commit drop as (
          select p.sku, rih.domain_id, rih.bizplace_id, rih.product_detail_id, coalesce(rih.uom, pd.uom, pd2.uom) as uom, 
          sum(rih.qty) as total_picked_qty, sum(rih.uom_value) as total_picked_uom_value
          from reduced_inventory_histories rih 
          inner join inventories inv on inv.domain_id = rih.domain_id and inv.pallet_id = rih.pallet_id
          left join product_details pd on pd.id = rih.product_detail_id
          left join product_details pd2 on pd2.product_id = rih.product_id and pd2.is_default = true
          left join products p on p.id = rih.product_id 
          where rih.domain_id = $1
          and inv.bizplace_id = $2
          and rih.created_at >= $3 and rih.created_at <= $4
          and rih.transaction_type = 'PICKING'
          group by p.sku, rih.domain_id, rih.bizplace_id, rih.product_detail_id, coalesce(rih.uom, pd.uom, pd2.uom)
        );
        `,
        [context.state.domain.id, bizplace.id, fromDate.value, toDate.value]
      )

      await tx.query(
        `
        create temp table tmp_stored_inv on commit drop as (
          select src2.sku, src2.domain_id, src2.bizplace_id, src2.pallet_id, src2.product_detail_id, src2.uom, 
          to_char(src2.created_at, 'YYYY-MM') as created_year_month, sum(closing_qty) as closing_qty from (
            select distinct on (rih.pallet_id) p.sku, rih.domain_id, rih.bizplace_id, rih.pallet_id, rih.product_detail_id,
            coalesce(rih.uom, pd.uom, pd2.uom) as uom, 
            rih.created_at, rih.transaction_type, rih.status, rih.opening_qty + rih.qty as closing_qty
            from reduced_inventory_histories rih 
            inner join inventories i on i.domain_id = rih.domain_id and i.pallet_id = rih.pallet_id 
            left join product_details pd on pd.id = rih.product_detail_id
            left join product_details pd2 on pd2.product_id = rih.product_id and pd2.is_default = true
            left join products p on p.id = rih.product_id 
            where rih.domain_id = $1 
            and i.bizplace_id = $2 
            and rih.created_at <= $3
            order by rih.pallet_id, rih.seq desc
          ) src2 where src2.status <> 'TERMINATED'
          and exists (
            select * from tmp_outbound_ord outboundOrd
            where outboundOrd.domain_id = src2.domain_id
              and outboundOrd.bizplace_id = src2.bizplace_id 
              and outboundOrd.product_detail_id = src2.product_detail_id 
              and outboundOrd.uom = src2.uom
          )
          group by src2.sku, src2.domain_id, src2.bizplace_id, src2.pallet_id, src2.product_detail_id, src2.uom, to_char(src2.created_at, 'YYYY-MM')
        );
        `,
        [context.state.domain.id, bizplace.id, toDate.value]
      )

      await tx.query(
        `
        create temp table temp_costing_report on commit drop as (
          WITH RECURSIVE cte_forwarded_cost as (
            select src.domain_id, src.bizplace_id, src.product_id, src.product_detail_id, src.uom, TO_DATE(src.created_year_month,'YYYY-MM')::timestamp as running_year_month,
            sum(src.qty) as qty, sum(src.total_cost)/ sum(src.qty) as monthly_avg_cost, 
            coalesce(sum(src.qty)) as closing_qty from (
              select distinct on (pallet_id) 
              ih.domain_id, ih.bizplace_id, ih.pallet_id, ih.unit_cost, (ih.unit_cost * (ih.opening_qty + ih.qty)) as total_cost, ih.status, ih.transaction_type, ih.product_id, ih.product_detail_id, coalesce(ih.uom, pd.uom) as uom, 
              ih.created_at, to_char(ih.created_at, 'YYYY-MM') as created_year_month,
              ih.opening_qty, 
              ih.qty as transaction_qty, 
              ih.opening_qty + ih.qty as qty, 
              ih.opening_uom_value, 
              ih.uom_value as transaction_uom_value, 
              ih.opening_uom_value + ih.uom_value as uom_value
              from reduced_inventory_histories ih 
              inner join inventories i on i.domain_id = ih.domain_id and i.pallet_id = ih.pallet_id 
              left join product_details pd on pd.id = ih.product_detail_id
              where ih.domain_id = $1 
              and i.bizplace_id = $2 
              and ih.created_at <= $3 and ih.unit_cost is not null
              and exists (
                select * from tmp_outbound_ord outboundOrd
                  where outboundOrd.domain_id = ih.domain_id
                    and outboundOrd.bizplace_id = ih.bizplace_id 
                    and outboundOrd.product_detail_id = ih.product_detail_id
                    and outboundOrd.uom = ih.uom
              )
              order by ih.pallet_id, ih.seq
            ) src
            group by src.domain_id, src.bizplace_id, src.product_id, src.product_detail_id, src.uom, src.created_year_month
            union all
            select dt1.domain_id, dt1.bizplace_id, dt1.product_id, dt1.product_detail_id, dt1.uom, 
            dt1.running_year_month + interval '1 month' as running_year_month, dt1.qty, dt1.monthly_avg_cost, dt1.closing_qty 
            from cte_forwarded_cost dt1
            where dt1.running_year_month + interval '1 month' < $3
          ) select fc.*, -outboundord.total_picked_qty as "qty", -outboundord.total_picked_uom_value as "uom_value", (-outboundord.total_picked_qty * fc.weighted_avg_unit_cost) as "total_cost" from (
              select p.sku as "product_sku", p."name" as "product_name", p.description as "product_description", p."type" as "product_type",
              fc.domain_id, fc.bizplace_id, fc.product_detail_id, fc.uom, fc.running_year_month,
              ROUND(cast(sum(fc.monthly_avg_cost * fc.closing_qty) / sum(fc.qty) as numeric),2) as "weighted_avg_unit_cost" 
              from cte_forwarded_cost fc
              left join products p on p.id = fc.product_id 
              where running_year_month = to_date(to_char($3::date, 'YYYY-MM'),'YYYY-MM')::timestamp
              group by p.sku, p."name", p.description, p."type", fc.domain_id, fc.bizplace_id, fc.product_detail_id, fc.uom, fc.running_year_month
            ) fc 
            left join tmp_outbound_ord outboundord on outboundord.domain_id = fc.domain_id
            and outboundord.bizplace_id = fc.bizplace_id 
            and outboundord.product_detail_id = fc.product_detail_id 
            and outboundord.uom = fc.uom
        );
        `,
        [context.state.domain.id, bizplace.id, toDate.value]
      )

      const total: any = await tx.query(`
        select count(*) from temp_costing_report  
      `)

      const result: any = await tx.query(
        `
        select * from temp_costing_report
        offset $1 limit $2
      `,
        [(params.pagination.page - 1) * params.pagination.limit, params.pagination.limit]
      )

      let items = result.map(itm => {
        let data = {
          bizplace: {
            description: bizplace.description,
            name: bizplace.name
          },
          product: {
            id: itm.product_id,
            sku: itm.product_sku,
            name: itm.product_name,
            description: itm.product_description,
            type: itm.product_type
          },
          qty: itm.qty,
          uom: itm.uom,
          uomValue: itm.uom_value,
          weightedAvgUnitCost: itm.weighted_avg_unit_cost,
          totalCost: itm.total_cost
        }

        return data
      })

      return {
        items,
        total: total[0].count
      }
    } catch (error) {
      throw error
    }
  }
}
